From c24f7cbac6ac06644e9935a23526bfe3b063d66f Mon Sep 17 00:00:00 2001 From: Nurmuhammet Allanov Date: Sat, 15 Nov 2025 18:51:40 +0500 Subject: [PATCH] Refactor payment processing: update method names for clarity, enhance VisaMasterPaymentOrderRepository with payment validation logic, and improve Turkish translations for payment notifications. --- .../Repositories/CardOrderRepository.php | 2 +- .../HalkbankOnlinePaymentController.php | 2 +- .../Repositories/OnlinePaymentRepository.php | 25 ++++- ..._visa_master_payment_order_items_table.php | 40 +++++++ .../Actions/PayVisaMasterPaymentAction.php | 102 ++++++++++++++--- .../Models/VisaMasterPaymentOrder.php | 11 ++ .../Models/VisaMasterPaymentOrderItem.php | 57 ++++++++++ .../VisaMasterPaymentOrderRepository.php | 106 ++++++++++++++++++ lang/tk.json | 10 +- 9 files changed, 333 insertions(+), 22 deletions(-) create mode 100644 app/Modules/VisaMasterPaymentOrder/Database/Migrations/2025_11_14_184206_create_visa_master_payment_order_items_table.php create mode 100644 app/Modules/VisaMasterPaymentOrder/Models/VisaMasterPaymentOrderItem.php diff --git a/app/Modules/CardOrder/Repositories/CardOrderRepository.php b/app/Modules/CardOrder/Repositories/CardOrderRepository.php index 561550f..3e9f46b 100644 --- a/app/Modules/CardOrder/Repositories/CardOrderRepository.php +++ b/app/Modules/CardOrder/Repositories/CardOrderRepository.php @@ -20,7 +20,7 @@ class CardOrderRepository $branch = $record->branch; return OnlinePaymentRepository::make(relatedModel: $record) - ->paymentProvider( + ->setPaymentProvider( HalkbankOnlinePaymentRepository::make() ->setUsername($branch->billingUsername()) ->setPassword($branch->billingPassword()) diff --git a/app/Modules/HalkbankOnlinePayment/Controllers/HalkbankOnlinePaymentController.php b/app/Modules/HalkbankOnlinePayment/Controllers/HalkbankOnlinePaymentController.php index 33aeed9..41c4579 100644 --- a/app/Modules/HalkbankOnlinePayment/Controllers/HalkbankOnlinePaymentController.php +++ b/app/Modules/HalkbankOnlinePayment/Controllers/HalkbankOnlinePaymentController.php @@ -17,7 +17,7 @@ class HalkbankOnlinePaymentController extends Controller ]); $onlinePaymentRepository = OnlinePaymentRepository::make() - ->paymentProvider(new HalkbankOnlinePaymentRepository); + ->setPaymentProvider(new HalkbankOnlinePaymentRepository); $paymentStatus = $onlinePaymentRepository->checkPayment($request->string('orderId')); diff --git a/app/Modules/OnlinePayment/Repositories/OnlinePaymentRepository.php b/app/Modules/OnlinePayment/Repositories/OnlinePaymentRepository.php index b2958c7..5b65ab3 100644 --- a/app/Modules/OnlinePayment/Repositories/OnlinePaymentRepository.php +++ b/app/Modules/OnlinePayment/Repositories/OnlinePaymentRepository.php @@ -43,6 +43,11 @@ class OnlinePaymentRepository */ protected Response $response; + /** + * Online payment + */ + protected ?OnlinePayment $onlinePayment; + /** * If payment is successful */ @@ -94,13 +99,29 @@ class OnlinePaymentRepository /** * Payment provder */ - public function paymentProvider(PaymentProviderContract $provider): self + public function setPaymentProvider(PaymentProviderContract $provider): self { $this->provider = $provider; return $this; } + /** + * Payment provider + */ + public function paymentProvider(): PaymentProviderContract + { + return $this->provider; + } + + /** + * Online payment + */ + public function onlinePayment(): ?OnlinePayment + { + return $this->onlinePayment; + } + /** * If payment has been successfull */ @@ -180,7 +201,7 @@ class OnlinePaymentRepository $data['online_paymantable_type'] = $this->relatedModel::class; } - OnlinePayment::create($data); + $this->onlinePayment = OnlinePayment::create($data); } /** diff --git a/app/Modules/VisaMasterPaymentOrder/Database/Migrations/2025_11_14_184206_create_visa_master_payment_order_items_table.php b/app/Modules/VisaMasterPaymentOrder/Database/Migrations/2025_11_14_184206_create_visa_master_payment_order_items_table.php new file mode 100644 index 0000000..9d79c43 --- /dev/null +++ b/app/Modules/VisaMasterPaymentOrder/Database/Migrations/2025_11_14_184206_create_visa_master_payment_order_items_table.php @@ -0,0 +1,40 @@ +id(); + $table->foreignId('visa_master_payment_order_id')->constrained('visa_master_payment_orders')->nullOnDelete(); + $table->foreignId('online_payment_id')->nullable()->constrained('online_payments')->nullOnDelete(); + + $table->string('payer_name')->nullable(); + $table->string('payer_card')->nullable(); + + $table->string('payment_order_number')->nullable(); + + $table->string('tmt_payment_amount'); + $table->string('usd_payment_amount'); + $table->boolean('paid')->default(false); + $table->boolean('synced_with_system')->nullable()->default(false); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('visa_master_payment_order_items'); + } +}; diff --git a/app/Modules/VisaMasterPaymentOrder/Filament/Actions/PayVisaMasterPaymentAction.php b/app/Modules/VisaMasterPaymentOrder/Filament/Actions/PayVisaMasterPaymentAction.php index 3d90b4a..5b45812 100644 --- a/app/Modules/VisaMasterPaymentOrder/Filament/Actions/PayVisaMasterPaymentAction.php +++ b/app/Modules/VisaMasterPaymentOrder/Filament/Actions/PayVisaMasterPaymentAction.php @@ -5,11 +5,14 @@ namespace App\Modules\VisaMasterPaymentOrder\Filament\Actions; use App\Modules\CurrencyRate\Models\CurrencyRate; use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder; use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterSettings; +use App\Modules\VisaMasterPaymentOrder\Repositories\VisaMasterPaymentOrderRepository; use Filament\Actions\Action; use Filament\Forms\Components\TextInput; use Filament\Infolists\Components\TextEntry; +use Filament\Notifications\Notification; use Filament\Schemas\Components\Fieldset; use Filament\Schemas\Components\Utilities\Set; +use Livewire\Component; class PayVisaMasterPaymentAction { @@ -28,8 +31,11 @@ class PayVisaMasterPaymentAction return []; } - $bank_fee = 20; - $gbus_fee = 3; + $visaMasterPaymentOrderRepository = VisaMasterPaymentOrderRepository::make(); + + $bankFee = $visaMasterPaymentOrderRepository->bankFee(); + $gbusFee = $visaMasterPaymentOrderRepository->gbusFee(); + $max_value = number_format($usd_to_tmt * 250, 2); return [ @@ -46,11 +52,11 @@ class PayVisaMasterPaymentAction TextEntry::make('bank_fee') ->extraAttributes(['class' => 'uppercase tracking-wide font-bold text-xs']) - ->label(sprintf('Bankyň tutumy: %s TMT', $bank_fee)), + ->label(sprintf('Bankyň tutumy: %s TMT', $bankFee)), TextEntry::make('gbus_fee') ->extraAttributes(['class' => 'uppercase tracking-wide font-bold text-xs']) - ->label(sprintf('GBÜS tutumy: %s TMT', $gbus_fee)), + ->label(sprintf('GBÜS tutumy: %s TMT', $gbusFee)), ]), Fieldset::make() @@ -67,32 +73,94 @@ class PayVisaMasterPaymentAction ->maxValue($max_value) ->helperText(sprintf('Iň ýokary möçberi: %s TMT', $max_value)) ->live() - ->afterStateUpdated(function (Set $set, ?string $state) use ($usd_to_tmt) { + ->afterStateUpdated(function (Set $set, ?string $state) use ($usd_to_tmt, $bankFee, $gbusFee) { if (! $state || $state === 0 || $state === '') { $set('usd_rate', ''); return; } - $usd_rate = number_format($state / $usd_to_tmt, 2, '.', ''); - $total_amount = floatval(number_format($state, 2, '.', '')) + 23; + $usd_rate = floatval(number_format($state / $usd_to_tmt, 2, '.', '')); + $total_amount = floatval(number_format($state, 2, '.', '')) + $bankFee + $gbusFee; - $set('usd_rate', $usd_rate.' USD'); - $set('total_amount', $total_amount.' TMT'); + $set('usd_rate', $usd_rate); + $set('total_amount', $total_amount); }), - TextEntry::make('usd_rate') - ->label(__('USD ekwalendi')), + TextInput::make('usd_rate') + ->label(__('USD ekwalendi')) + ->suffix('USD') + ->readOnly(), - TextEntry::make('total_amount') - ->label(__('Total amount')), + TextInput::make('total_amount') + ->label(__('Total amount')) + ->suffix('TMT') + ->readOnly(), ]), ]; }) - ->action(function (array $data, VisaMasterPaymentOrder $record): void { - // $record->author()->associate($data['authorId']); - // $record->save(); - }); + ->action(function (array $data, VisaMasterPaymentOrder $record, Component $livewire): void { + /** @var array{payment_amount: float, usd_rate: float, total_amount: float} */ + $formData = $data; + + $visaMasterPaymentOrderRepository = VisaMasterPaymentOrderRepository::make(); + + $today = today(); + + // Needs to be tested... + if ($visaMasterPaymentOrderRepository->hasBeenPaidThisMonth($record, $today)) { + Notification::make() + ->title(__('This month has already been paid').'!') + ->body(__('This month has already been paid')) + ->danger() + ->send(); + + return; + } + + // Needs to be tested... + if ($visaMasterPaymentOrderRepository->isEndOfMonth($today)) { + Notification::make() + ->title(__('Payment cannot be accepted on the last day of the month').'!') + ->body(__('Payment cannot be accepted on the last day of the month')) + ->danger() + ->send(); + + return; + } + + // Needs to be tested... + if (! $record->branch || ! $record->branch->billing_visa_master_username) { + Notification::make() + ->title(__('Contact with the operator').'!') + ->body(__('Billing information is not available in the branch! The branch cannot accept Visa/Master payments.')) + ->danger() + ->send(); + + return; + } + + $total_amount = $formData['payment_amount'] + $visaMasterPaymentOrderRepository->bankFee() + $visaMasterPaymentOrderRepository->gbusFee(); + + $onlinePaymentRepository = $visaMasterPaymentOrderRepository->createOnlinePaymentOrder($record, $total_amount); + + if ($onlinePaymentRepository->failed()) { + Notification::make() + ->title(__('Payment error').'!') + ->body(sprintf('%s. %s. %s.', __('Problem connecting on HALKBANK SYSTEM'), __('Error has been captured'), __('Please try again later'))) + ->danger() + ->send(); + + return; + } + + $visaMasterPaymentOrderRepository->createPaymentRecord($record, $onlinePaymentRepository, $formData['usd_rate']); + + // Tell Livewire to open it in a new tab + $livewire->js("window.open('{$onlinePaymentRepository->paymentLink()}', '_blank')"); + + }) + ->modalSubmitActionLabel(__('Pay')); } } diff --git a/app/Modules/VisaMasterPaymentOrder/Models/VisaMasterPaymentOrder.php b/app/Modules/VisaMasterPaymentOrder/Models/VisaMasterPaymentOrder.php index 87f7ab6..5564ffc 100644 --- a/app/Modules/VisaMasterPaymentOrder/Models/VisaMasterPaymentOrder.php +++ b/app/Modules/VisaMasterPaymentOrder/Models/VisaMasterPaymentOrder.php @@ -6,6 +6,7 @@ use App\Models\User; use App\Modules\Branch\Models\Branch; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; @@ -93,6 +94,16 @@ class VisaMasterPaymentOrder extends Model implements HasMedia return $this->belongsTo(Branch::class); } + /** + * Payment itmes + * + * @return HasMany + */ + public function paymentItems(): HasMany + { + return $this->hasMany(VisaMasterPaymentOrderItem::class, 'visa_master_payment_order_id'); + } + /** * Get applications types * diff --git a/app/Modules/VisaMasterPaymentOrder/Models/VisaMasterPaymentOrderItem.php b/app/Modules/VisaMasterPaymentOrder/Models/VisaMasterPaymentOrderItem.php new file mode 100644 index 0000000..7b63b8b --- /dev/null +++ b/app/Modules/VisaMasterPaymentOrder/Models/VisaMasterPaymentOrderItem.php @@ -0,0 +1,57 @@ + + */ + public function parent(): BelongsTo + { + return $this->belongsTo(VisaMasterPaymentOrder::class, 'visa_master_payment_order_id'); + } + + /** + * Online payment + * + * @return BelongsTo + */ + public function onlinePayment(): BelongsTo + { + return $this->belongsTo(OnlinePayment::class, 'online_payment_id'); + } +} diff --git a/app/Modules/VisaMasterPaymentOrder/Repositories/VisaMasterPaymentOrderRepository.php b/app/Modules/VisaMasterPaymentOrder/Repositories/VisaMasterPaymentOrderRepository.php index 7884401..19004d9 100644 --- a/app/Modules/VisaMasterPaymentOrder/Repositories/VisaMasterPaymentOrderRepository.php +++ b/app/Modules/VisaMasterPaymentOrder/Repositories/VisaMasterPaymentOrderRepository.php @@ -2,9 +2,115 @@ namespace App\Modules\VisaMasterPaymentOrder\Repositories; +use App\Modules\HalkbankOnlinePayment\Repositories\HalkbankOnlinePaymentRepository; use App\Modules\Makeable; +use App\Modules\OnlinePayment\Repositories\OnlinePaymentRepository; +use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder; +use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrderItem; +use Illuminate\Support\Carbon; class VisaMasterPaymentOrderRepository { use Makeable; + + /** + * Bank fee + */ + public int|string|float $bankFee = 20; + + /** + * Gbus fee + */ + public int|string|float $gbusFee = 3; + + /** + * Bank fee + */ + public function bankFee(): int|string|float + { + return $this->bankFee; + } + + /** + * Gbus fee + */ + public function gbusFee(): int|string|float + { + return $this->gbusFee; + } + + /** + * Has been paid this month + */ + public function hasBeenPaidThisMonth(VisaMasterPaymentOrder $record, Carbon $today): bool + { + return $record->paymentItems->contains( + fn (VisaMasterPaymentOrderItem $item): bool => $item->paid && $item->created_at->format('m Y') === $today->format('m Y') + ); + } + + /** + * Is end of month + */ + public function isEndOfMonth(Carbon $today): bool + { + $lastDay = $today->copy()->endOfMonth(); + $lastDayOfWeek = $lastDay->format('l'); + + $oneDayBefore = $lastDay->copy()->subDay(); // Saturday... + $twoDaysBefore = $lastDay->copy()->subDays(2); // Friday... + + // Condition 1: Today is the last day + if ($today->isSameDay($lastDay)) { + return true; + } + + // Condition 2: Last day is Sunday → forbid Friday & Saturday + if ($lastDayOfWeek === 'Sunday' && ($today->isSameDay($oneDayBefore) || $today->isSameDay($twoDaysBefore))) { + return true; + } + + // Condition 3: Last day is Saturday → forbid Friday + if ($lastDayOfWeek === 'Saturday' && $today->isSameDay($oneDayBefore)) { + return true; + } + + // Default: allow payment + return false; + } + + /** + * Create online payment order + */ + public function createOnlinePaymentOrder(VisaMasterPaymentOrder $record, int|float|string $amount): OnlinePaymentRepository + { + /** @var \App\Modules\Branch\Models\Branch */ + $branch = $record->branch; + + return OnlinePaymentRepository::make(relatedModel: $record) + ->setPaymentProvider( + HalkbankOnlinePaymentRepository::make( + username: $branch->billingUsername(), + password: $branch->billingPassword(), + amount: $amount, + returnUrl: route('halkbank-online-payment.store'), + description: 'Visa/Master tölegi' + ) + ) + ->sendRequest(); + } + + /** + * Create payment record + */ + public function createPaymentRecord(VisaMasterPaymentOrder $record, OnlinePaymentRepository $onlinePaymentRepository, int|string|float $usdRate): void + { + VisaMasterPaymentOrderItem::create([ + 'visa_master_payment_order_id' => $record->id, + 'online_payment_id' => $onlinePaymentRepository->onlinePayment()->id, + 'payment_order_number' => $onlinePaymentRepository->onlinePayment()->orderNumber, + 'tmt_payment_amount' => $onlinePaymentRepository->paymentProvider()->amount(), + 'usd_payment_amount' => $usdRate, + ]); + } } diff --git a/lang/tk.json b/lang/tk.json index f136ab5..d268cee 100644 --- a/lang/tk.json +++ b/lang/tk.json @@ -714,5 +714,13 @@ "Content": "Mazmuny", "Payment month": "Töleg aýy", "Make payment for:": "Töleg üçin:", - "Payment amount": "Töleg möçberi" + "Payment amount": "Töleg möçberi", + "This month has already been paid": "Şul aý töleg edildi", + "Contact with the operator": "Operator bilen habarlaşyň!", + "Billing information is not available in the branch! The branch cannot accept Visa/Master payments.": "Billing maglumatlary şahamçada ýok! Şahamça visa/master tölegi kabul edip bilmeýär.", + "Payment cannot be accepted on the last day of the month": "Töleg aýyň son döredilen güni töleýär", + "Payment error": "Töleg näsazlygy", + "Problem connecting on HALKBANK SYSTEM": "Halkbank ulgamyna birikmekde näsazlyk", + "Error has been captured": "Näsazlyk ulgam tarapyndan ýazga alyndy", + "Please try again later": "Täzeden geçirmek üçin synanyşyňyz" }