diff --git a/resources/lang/ru/messages.php b/resources/lang/ru/messages.php new file mode 100644 index 0000000..e2e739c --- /dev/null +++ b/resources/lang/ru/messages.php @@ -0,0 +1,151 @@ + 'Магазин', + 'welcome' => 'Добро пожаловать в магазин!', + + 'buy' => 'Купить', + + 'free' => 'Бесплатно', + + 'periods' => [ + 'hours' => 'часов', + 'days' => 'дней', + 'weeks' => 'недель', + 'months' => 'месяцев', + 'years' => 'лет', + ], + + 'fields' => [ + 'balance' => 'Баланс', + 'category' => 'Категория', + 'code' => 'Код', + 'commands' => 'Команды', + 'currency' => 'Валюта', + 'discount' => 'Скидка', + 'expire_date' => 'Дата окончания', + 'gateways' => 'Платежные системы', + 'global_limit' => 'Глобальный лимит покупок', + 'original_balance' => 'Начальный баланс', + 'package' => 'Товар', + 'packages' => 'Товары', + 'payment_id' => 'ID платежа', + 'payments' => 'Платежи', + 'price' => 'Цена', + 'quantity' => 'Количество', + 'renewal_date' => 'Дата продления', + 'required' => 'Обязательно', + 'required_packages' => 'Необходимые товары', + 'required_roles' => 'Необходимая роль', + 'role' => 'Роль после покупки', + 'start_date' => 'Дата начала', + 'subscription' => 'Подписка', + 'subscription_id' => 'ID подписки', + 'total' => 'Итого', + 'user_id' => 'ID пользователя', + 'user_limit' => 'Лимит покупок пользователя', + ], + + 'actions' => [ + 'subscribe' => 'Подписаться', + 'manage' => 'Управлять подпиской', + 'move' => 'Переместить', + ], + + 'goal' => [ + 'title' => 'Цель месяца', + 'progress' => 'Выполнено: :count%', + ], + + 'recent' => [ + 'title' => 'Последние платежи', + 'empty' => 'Нет недавних платежей', + ], + + 'top' => [ + 'title' => 'Лучший покупатель', + ], + + 'cart' => [ + 'title' => 'Корзина', + 'success' => 'Покупка успешно завершена.', + 'guest' => 'Для покупки необходимо войти.', + 'empty' => 'Ваша корзина пуста.', + 'checkout' => 'Оформить', + 'clear' => 'Очистить корзину', + 'pay' => 'Оплатить', + 'back' => 'Назад в магазин', + 'total' => 'Итого: :total', + 'payable_total' => 'К оплате: :total', + 'credit' => 'Кредит', + + 'confirm' => [ + 'title' => 'Оплатить?', + 'price' => 'Вы уверены, что хотите купить корзину за :price?', + ], + + 'errors' => [ + 'money' => 'У вас недостаточно средств для покупки.', + 'execute' => 'Произошла ошибка при оплате, покупка отменена.', + ], + ], + + 'coupons' => [ + 'title' => 'Купоны', + 'add' => 'Добавить купон', + 'error' => 'Купон не существует, истёк или больше не может быть использован.', + 'cumulate' => 'Вы не можете использовать этот купон с другим.', + ], + + 'payment' => [ + 'title' => 'Оплата', + 'manual' => 'Ручная оплата', + + 'empty' => 'Нет доступных методов оплаты.', + + 'info' => 'Покупка №:id на :website: :items', + 'subscription' => ':package - Подписка (Пользователь #:user)', + 'error' => 'Не удалось выполнить платеж, попробуйте позже.', + 'giftcards' => 'Подарочные карты', + + 'success' => 'Платеж завершён, вы получите покупку в игре менее чем через минуту.', + 'pending' => 'Платеж ожидает подтверждения, покупка будет доставлена после подтверждения.', + + 'webhook' => 'Новый платеж в магазине', + 'webhook_info' => 'Сумма: :total, ID: :id (:gateway)', + 'webhook_chargeback' => 'Чарджбэк платежа в магазине', + 'webhook_refund' => 'Возврат платежа в магазине', + ], + + 'categories' => [ + 'empty' => 'Эта категория пуста.', + ], + + 'packages' => [ + 'limit' => 'Вы купили максимальное количество этого товара.', + 'requirements' => 'Вы не соответствуете требованиям для покупки этого товара.', + 'cumulate' => 'Вы не можете купить этот товар вместе с другим из той же категории в одной покупке.', + 'file' => 'Нажмите здесь, чтобы скачать файл :file', + ], + + 'offers' => [ + 'gateway' => 'Тип оплаты', + 'amount' => 'Сумма', + + 'empty' => 'Нет доступных предложений.', + ], + + 'profile' => [ + 'payments' => 'Ваши платежи', + 'subscriptions' => 'Ваши подписки', + 'money' => 'Деньги: :balance', + ], + + 'giftcards' => [ + 'title' => 'Подарочные карты', + 'error' => 'Подарочная карта не существует, истекла или больше не может быть использована.', + 'add' => 'Добавить подарочную карту', + 'notification' => 'Вы получили подарочную карту, код: :code (:balance).', + 'pending' => 'Платеж для этой карты уже начат. Завершите платеж или подождите несколько минут.', + ], +]; diff --git a/resources/views/admin/settings.blade.php b/resources/views/admin/settings.blade.php index 766961f..b88e72c 100644 --- a/resources/views/admin/settings.blade.php +++ b/resources/views/admin/settings.blade.php @@ -29,6 +29,16 @@
{{ trans('shop::admin.settings.use_site_money_info') }}
+
+ + +
+ +
+ + +
+
diff --git a/resources/views/cart/_auth.blade.php b/resources/views/cart/_auth.blade.php new file mode 100644 index 0000000..ccaa44e --- /dev/null +++ b/resources/views/cart/_auth.blade.php @@ -0,0 +1,62 @@ + +
+ @if(oauth_login()) + + @else +
+
+
Вход
+
+ @csrf +
+ + + @error('email'){{ $message }}@enderror +
+
+ + + @error('password'){{ $message }}@enderror +
+
+ + +
+ @includeWhen($captchaLogin, 'elements.captcha', ['center' => true]) + +
+
+
+
Регистрация
+
+ @csrf +
+ + + @error('name'){{ $message }}@enderror +
+
+ + + @error('email'){{ $message }}@enderror +
+
+ + + @error('password'){{ $message }}@enderror +
+
+ + +
+ @includeWhen($captchaRegister, 'elements.captcha', ['center' => true]) + +
+
+
+ @endif +
diff --git a/resources/views/cart/index.blade.php b/resources/views/cart/index.blade.php index 6fd9513..4098b68 100644 --- a/resources/views/cart/index.blade.php +++ b/resources/views/cart/index.blade.php @@ -19,6 +19,9 @@
+ @if(auth()->guest() && setting('shop.cart_auth')) + @include('shop::cart._auth') + @endif @if(! $cart->isEmpty())
@csrf @@ -212,15 +215,19 @@ {{ trans('shop::messages.cart.back') }} - @if(use_site_money()) - + @auth + @if(use_site_money()) + + @else + + @endif @else - - @endif + Войдите, чтобы оплатить + @endauth
diff --git a/resources/views/packages/show.blade.php b/resources/views/packages/show.blade.php index b024973..4af5570 100644 --- a/resources/views/packages/show.blade.php +++ b/resources/views/packages/show.blade.php @@ -68,9 +68,29 @@ @endif @else - +
+ @csrf + + @if($package->custom_price) + + +
+ +
+ @endif + + @if($package->has_quantity) + + +
+ +
+ @endif + + +
@endauth
diff --git a/routes/web.php b/routes/web.php index aea28c5..c9f6dfa 100644 --- a/routes/web.php +++ b/routes/web.php @@ -9,6 +9,7 @@ use Azuriom\Plugin\Shop\Controllers\PaymentController; use Azuriom\Plugin\Shop\Controllers\ProfileController; use Azuriom\Plugin\Shop\Controllers\SubscriptionController; +use Azuriom\Plugin\Shop\Controllers\CartAuthController; use Illuminate\Support\Facades\Route; /* @@ -30,11 +31,11 @@ Route::resource('packages', PackageController::class)->only('show'); -Route::prefix('packages/{package}')->name('packages.')->middleware('auth')->group(function () { +Route::prefix('packages/{package}')->name('packages.')->group(function () { Route::post('/buy', [PackageController::class, 'buy'])->name('buy'); Route::get('/options', [PackageController::class, 'showVariables']); Route::post('/options', [PackageController::class, 'buy'])->name('variables'); - Route::get('/files/{file}', [PackageController::class, 'downloadFile'])->name('file'); + Route::get('/files/{file}', [PackageController::class, 'downloadFile'])->middleware('auth')->name('file'); }); Route::prefix('offers')->name('offers.')->middleware('verified')->group(function () { @@ -43,9 +44,10 @@ Route::post('/{offer:id}/{gateway:type}', [OfferController::class, 'pay'])->name('pay'); }); -Route::prefix('cart')->name('cart.')->middleware('auth')->group(function () { +Route::prefix('cart')->name('cart.')->group(function () { Route::get('/', [CartController::class, 'index'])->name('index'); Route::post('/', [CartController::class, 'update'])->name('update'); + Route::post('/register', [CartAuthController::class, 'register'])->name('register'); // TODO Match multiple methods is not really good here... Route::match(['GET', 'POST'], '/remove/{package}', [CartController::class, 'remove'])->name('remove'); Route::post('/clear', [CartController::class, 'clear'])->name('clear'); diff --git a/src/Controllers/Admin/SettingController.php b/src/Controllers/Admin/SettingController.php index de81ccb..7fd3aed 100644 --- a/src/Controllers/Admin/SettingController.php +++ b/src/Controllers/Admin/SettingController.php @@ -34,6 +34,8 @@ public function show() 'enableHome' => setting('shop.home.enabled', true), 'homeMessage' => setting('shop.home', ''), 'termsRequired' => old('terms_required', setting('shop.required_terms') !== null), + 'cartAuth' => setting('shop.cart_auth', false), + 'emailVerification' => setting('shop.email_verification', true), ]); } @@ -65,6 +67,8 @@ public function save(Request $request) 'shop.webhook' => $request->input('webhook'), 'shop.home' => $request->input('home_message'), 'shop.home.enabled' => $request->has('enable_home'), + 'shop.cart_auth' => $request->has('cart_auth'), + 'shop.email_verification' => $request->has('email_verification'), 'shop.commands' => is_array($commands) ? json_encode($commands) : null, 'shop.required_terms' => $request->filled('terms_required') ? $request->input('terms') : null, ]); diff --git a/src/Controllers/CartAuthController.php b/src/Controllers/CartAuthController.php new file mode 100644 index 0000000..4d7546b --- /dev/null +++ b/src/Controllers/CartAuthController.php @@ -0,0 +1,74 @@ +validator($request->all())->validate(); + + $user = $this->create($request->all()); + + event(new Registered($user)); + + // Если подтверждение почты отключено, помечаем её подтвержденной + if (!setting('shop.email_verification', true)) { + $user->markEmailAsVerified(); + } + + Auth::login($user); + + return redirect()->intended(route('shop.cart.index')); + } + + /** + * Правила валидации при регистрации. + */ + protected function validator(array $data) + { + $rules = [ + 'name' => ['required', 'string', 'max:25', 'unique:users', new Username()], + 'email' => ['required', 'string', 'email', 'max:50', 'unique:users'], + 'password' => ['required', 'confirmed', Password::default()], + ]; + + // Проверяем имя пользователя через игру, если она установлена + if (game()->id() !== 'none') { + $rules['name'][] = new GameAuth(); + } + + return Validator::make($data, $rules); + } + + /** + * Создание пользователя. + */ + protected function create(array $data): User + { + return User::forceCreate([ + 'name' => $data['name'], + 'email' => $data['email'], + 'password' => $data['password'], + 'role_id' => Role::defaultRoleId(), + 'game_id' => game()->getUserUniqueId($data['name']), + 'last_login_ip' => FacadesRequest::ip(), + 'last_login_at' => now(), + ]); + } +} diff --git a/src/Controllers/CartController.php b/src/Controllers/CartController.php index 4eb6f89..dcd5a40 100644 --- a/src/Controllers/CartController.php +++ b/src/Controllers/CartController.php @@ -18,6 +18,16 @@ class CartController extends Controller */ public function index(Request $request) { + // Если отключена гостевая корзина, перенаправляем на страницу входа + if (!setting('shop.cart_auth', false) && auth()->guest()) { + return redirect()->route('login'); + } + + // Запоминаем корзину как запрошенную страницу для перенаправления после входа + if (auth()->guest()) { + $request->session()->put('url.intended', route('shop.cart.index')); + } + $terms = setting('shop.required_terms'); if ($terms !== null) { @@ -29,6 +39,9 @@ public function index(Request $request) return view('shop::cart.index', [ 'cart' => Cart::fromSession($request->session()), 'terms' => $terms, + // Передаем настройки капчи для форм авторизации и регистрации + 'captchaLogin' => (bool) setting('captcha.login'), + 'captchaRegister' => setting('captcha.type') !== null, ]); } @@ -37,6 +50,10 @@ public function index(Request $request) */ public function remove(Request $request, Package $package) { + if (!setting('shop.cart_auth', false) && auth()->guest()) { + return redirect()->route('login'); + } + $cart = Cart::fromSession($request->session()); $cart->remove($package); @@ -49,6 +66,10 @@ public function remove(Request $request, Package $package) */ public function update(Request $request) { + if (!setting('shop.cart_auth', false) && auth()->guest()) { + return redirect()->route('login'); + } + $cart = Cart::fromSession($request->session()); foreach ($request->input('quantities', []) as $id => $quantity) { @@ -69,6 +90,10 @@ public function update(Request $request) */ public function clear(Request $request) { + if (!setting('shop.cart_auth', false) && auth()->guest()) { + return redirect()->route('login'); + } + Cart::fromSession($request->session())->clear(); return to_route('shop.cart.index'); diff --git a/src/Controllers/PackageController.php b/src/Controllers/PackageController.php index 05cf896..935682a 100644 --- a/src/Controllers/PackageController.php +++ b/src/Controllers/PackageController.php @@ -40,6 +40,15 @@ public function showVariables(Request $request, Package $package) */ public function buy(Request $request, Package $package) { + // Если гостевая корзина отключена, требуем авторизацию + if (!setting('shop.cart_auth', false) && auth()->guest()) { + return redirect()->route('login'); + } + + if (auth()->guest()) { + $request->session()->put('url.intended', route('shop.cart.index')); + } + $this->validate($request, [ 'quantity' => 'nullable|integer', 'price' => 'sometimes|nullable|numeric|min:'.$package->price, @@ -53,11 +62,11 @@ public function buy(Request $request, Package $package) $user = $request->user(); $cart = Cart::fromSession($request->session()); - if ($package->getMaxQuantity() < 1 || $package->category->hasReachLimit($user)) { + if ($package->getMaxQuantity() < 1 || ($user !== null && $package->category->hasReachLimit($user))) { return redirect()->back()->with('error', trans('shop::messages.packages.limit')); } - if (! $package->hasBoughtRequirements() || ! $package->hasRequiredRole($user->role)) { + if ($user !== null && (! $package->hasBoughtRequirements() || ! $package->hasRequiredRole($user->role))) { return redirect()->back()->with('error', trans('shop::messages.packages.requirements')); }