донат шоп, управление магазином, яндекс метрика

This commit is contained in:
2026-07-19 19:17:29 +04:00
parent 8bf1b852f0
commit df7f98fe8b
48 changed files with 1859 additions and 18 deletions
@@ -0,0 +1,59 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AdjustBalanceRequest;
use App\Services\AzerothCoreAccountService;
use App\Services\BalanceService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
use InvalidArgumentException;
class BalanceController extends Controller
{
public function index(Request $request, BalanceService $balances): View
{
$search = trim((string) $request->query('search', ''));
return view('admin.balances.index', [
'transactions' => $balances->adminTransactions($search !== '' ? $search : null),
'search' => $search,
]);
}
public function adjust(
AdjustBalanceRequest $request,
AzerothCoreAccountService $accounts,
BalanceService $balances,
): RedirectResponse {
$accountInput = trim($request->string('account')->toString());
$account = ctype_digit($accountInput)
? $accounts->findUserById((int) $accountInput)
: $accounts->findUserByUsername($accountInput);
if (! $account) {
return back()->withInput()->withErrors(['account' => 'Игровой аккаунт не найден.']);
}
try {
$transaction = $balances->adjustByAdmin(
$account->id,
$request->string('amount')->toString(),
$request->string('direction')->toString(),
$request->user()->id,
$request->user()->username,
$request->string('reason')->toString(),
);
} catch (InvalidArgumentException $exception) {
return back()->withInput()->withErrors(['amount' => $exception->getMessage()]);
}
return back()->with('status', sprintf(
'Счёт %s изменён. Теперь на нём %s Слёз Элуны.',
$account->username,
number_format((float) $transaction->balance_after, 2, ',', ' '),
));
}
}
@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\SaveShopCategoryRequest;
use App\Http\Requests\Admin\SaveShopProductRequest;
use App\Models\ShopCategory;
use App\Models\ShopProduct;
use App\Services\ShopCatalogService;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class ShopController extends Controller
{
public function index(ShopCatalogService $catalog): View
{
return view('admin.shop.index', [
'categories' => $catalog->categories(),
'products' => $catalog->products(),
]);
}
public function storeCategory(SaveShopCategoryRequest $request, ShopCatalogService $catalog): RedirectResponse
{
$catalog->createCategory($request->validated());
return back()->with('status', 'Категория создана.');
}
public function updateCategory(ShopCategory $category, SaveShopCategoryRequest $request, ShopCatalogService $catalog): RedirectResponse
{
$catalog->updateCategory($category, $request->validated());
return back()->with('status', 'Категория обновлена.');
}
public function destroyCategory(ShopCategory $category, ShopCatalogService $catalog): RedirectResponse
{
$catalog->deleteCategory($category);
return back()->with('status', 'Категория удалена.');
}
public function storeProduct(SaveShopProductRequest $request, ShopCatalogService $catalog): RedirectResponse
{
$catalog->createProduct($request->validated());
return back()->with('status', 'Товар создан.');
}
public function updateProduct(ShopProduct $product, SaveShopProductRequest $request, ShopCatalogService $catalog): RedirectResponse
{
$catalog->updateProduct($product, $request->validated());
return back()->with('status', 'Товар обновлён.');
}
public function destroyProduct(ShopProduct $product, ShopCatalogService $catalog): RedirectResponse
{
$catalog->deleteProduct($product);
return back()->with('status', 'Товар удалён.');
}
}
@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Services\BalanceService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use InvalidArgumentException;
class GameBalanceController extends Controller
{
public function show(int $accountId, BalanceService $balances): JsonResponse
{
return response()->json(['account_id' => $accountId, 'balance' => $balances->balance($accountId)]);
}
public function spend(Request $request, int $accountId, BalanceService $balances): JsonResponse
{
$data = $request->validate([
'amount' => ['required', 'numeric', 'decimal:0,2', 'gt:0'],
'idempotency_key' => ['required', 'string', 'max:160'],
'description' => ['nullable', 'string', 'max:255'],
]);
try {
$transaction = $balances->spend(
$accountId,
(string) $data['amount'],
'game:'.$data['idempotency_key'],
$data['description'] ?? null,
);
} catch (InvalidArgumentException $exception) {
return response()->json(['message' => $exception->getMessage()], 422);
}
return response()->json([
'transaction_id' => $transaction->id,
'account_id' => $transaction->account_id,
'amount' => $transaction->amount,
'balance' => $transaction->balance_after,
]);
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Services\RobokassaService;
use Illuminate\Http\Request;
use InvalidArgumentException;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
class RobokassaController extends Controller
{
public function result(Request $request, RobokassaService $robokassa): Response
{
try {
$invoice = $robokassa->processResult($request->all());
} catch (InvalidArgumentException $exception) {
report($exception);
return response('bad signature', 400)->header('Content-Type', 'text/plain');
} catch (Throwable $exception) {
report($exception);
return response('temporary error', 503)->header('Content-Type', 'text/plain');
}
return response('OK'.$invoice->id)->header('Content-Type', 'text/plain');
}
}
+7 -1
View File
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Http\Requests\ChangePasswordRequest;
use App\Services\AzerothCoreAccountService;
use App\Services\BalanceService;
use App\Services\GameClientDownloadService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -12,7 +13,7 @@ use Throwable;
class CabinetController extends Controller
{
public function index(Request $request, AzerothCoreAccountService $accounts): View
public function index(Request $request, AzerothCoreAccountService $accounts, BalanceService $balances): View
{
$user = $request->user();
@@ -20,6 +21,11 @@ class CabinetController extends Controller
'user' => $user,
'characters' => $accounts->charactersForAccount($user->id),
'clientFilename' => config('moonwell.client.object_key'),
'balance' => $balances->balance($user->id),
'balanceTransactions' => $balances->recentTransactions($user->id),
'minimumDeposit' => config('services.robokassa.minimum_amount'),
'maximumDeposit' => config('services.robokassa.maximum_amount'),
'tearsPerRuble' => config('services.robokassa.tears_per_ruble'),
]);
}
+11 -6
View File
@@ -78,6 +78,7 @@ class LandingController extends Controller
'moon' => true,
],
'registerEndpoint' => route('game-account.store'),
'inviteRequired' => (bool) config('moonwell.registration.require_invite_code'),
'csrfToken' => csrf_token(),
'auth' => [
'authenticated' => $user !== null,
@@ -110,15 +111,19 @@ class LandingController extends Controller
): RedirectResponse
{
try {
$result = $inviteCodes->redeemForRegistration(
$request->string('invite_code')->toString(),
$request->string('username')->toString(),
fn (): array => $registrar->register(
$register = fn (): array => $registrar->register(
$request->string('username')->toString(),
$request->string('email')->toString(),
$request->string('password')->toString(),
),
);
);
$result = config('moonwell.registration.require_invite_code')
? $inviteCodes->redeemForRegistration(
$request->string('invite_code')->toString(),
$request->string('username')->toString(),
$register,
)
: $register();
} catch (DuplicateGameAccountException $exception) {
return back()
->withErrors([$exception->field => $exception->getMessage()])
@@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers;
use App\Services\RobokassaService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
class PaymentController extends Controller
{
public function create(Request $request, RobokassaService $robokassa): View
{
$validated = $request->validate([
'amount' => [
'required',
'numeric',
'decimal:0,2',
Rule::numeric()->min((float) config('services.robokassa.minimum_amount'))->max((float) config('services.robokassa.maximum_amount')),
],
]);
$user = $request->user();
$invoice = $robokassa->createInvoice($user->id, (string) $validated['amount']);
return view('payments.redirect', [
'paymentUrl' => config('services.robokassa.payment_url'),
'parameters' => $robokassa->paymentParameters($invoice, $user->email),
]);
}
public function success(): RedirectResponse
{
return redirect()->route('cabinet.index')->with('status', 'Пополнение принято. Слёзы Элуны скоро появятся на счёте.');
}
public function fail(): RedirectResponse
{
return redirect()->route('cabinet.index')->with('error', 'Оплата отменена или не была завершена.');
}
}