From 8002679ca23f5557bf4c1ff1d72321a388fff0f1 Mon Sep 17 00:00:00 2001 From: hasanzaki Date: Sat, 4 Jul 2026 15:52:01 +0330 Subject: [PATCH] check status | sync status --- backend/app/Enums/PaymentStatusEnum.php | 21 ++-- backend/app/Models/FibTransaction.php | 11 ++ backend/app/Models/Transaction.php | 6 + .../FIB/CheckPaymentStatusService.php | 27 ++--- .../Commands/SyncFibPaymentStatuses.php | 20 ++++ .../Controllers/FIBTempController.php | 40 +++++++ .../app/Services/Payments/PaymentService.php | 24 ++-- .../Payments/SyncFibPaymentStatusService.php | 113 ++++++++++++++++++ .../Controllers/Payment/PaymentController.php | 2 +- backend/bootstrap/app.php | 12 +- .../database/seeders/PaymentStatusSeeder.php | 14 ++- backend/routes/api.php | 9 +- backend/routes/console.php | 6 + 13 files changed, 255 insertions(+), 50 deletions(-) create mode 100644 backend/app/Services/Payments/Commands/SyncFibPaymentStatuses.php create mode 100644 backend/app/Services/Payments/Controllers/FIBTempController.php create mode 100644 backend/app/Services/Payments/SyncFibPaymentStatusService.php diff --git a/backend/app/Enums/PaymentStatusEnum.php b/backend/app/Enums/PaymentStatusEnum.php index 24e0e9a6..551fdee3 100644 --- a/backend/app/Enums/PaymentStatusEnum.php +++ b/backend/app/Enums/PaymentStatusEnum.php @@ -4,23 +4,26 @@ namespace App\Enums; enum PaymentStatusEnum: int { - case Unpaid = 0; + case UNPAID = 0; case Cancelled = 1; case Expired = 2; - case Paid = 3; - case Refunded = 4; + case PAID = 3; + case REFUNDED = 4; case PendingVerification = 5; + case REFUND_REQUESTED = 6; + public function label(): string { return match ($this) { - self::Unpaid => 'unpaid', - self::Cancelled => 'cancelled', - self::Expired => 'expired', - self::Paid => 'paid', - self::Refunded => 'refunded', - self::PendingVerification => 'pending_verification', + self::UNPAID => 'UNPAID', + self::Cancelled => 'CANCELLED', + self::Expired => 'EXPIRED', + self::PAID => 'PAID', + self::REFUNDED => 'REFUNDED', + self::REFUND_REQUESTED => 'REFUND_REQUESTED', + self::PendingVerification => 'PENDING_VERIFICATION', }; } } diff --git a/backend/app/Models/FibTransaction.php b/backend/app/Models/FibTransaction.php index c208c0a7..897e527b 100644 --- a/backend/app/Models/FibTransaction.php +++ b/backend/app/Models/FibTransaction.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class FibTransaction extends Model { @@ -11,4 +12,14 @@ class FibTransaction extends Model use HasFactory; protected $guarded = ['id']; + + public function transaction(): BelongsTo + { + return $this->belongsTo(Transaction::class); + } + + public function payment(): BelongsTo + { + return $this->belongsTo(Payment::class); + } } diff --git a/backend/app/Models/Transaction.php b/backend/app/Models/Transaction.php index a685ed3d..3c909303 100644 --- a/backend/app/Models/Transaction.php +++ b/backend/app/Models/Transaction.php @@ -5,6 +5,7 @@ namespace App\Models; use Database\Factories\TransactionFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class Transaction extends Model { @@ -12,4 +13,9 @@ class Transaction extends Model use HasFactory; protected $guarded = ['id']; + + public function payment(): BelongsTo + { + return $this->belongsTo(Payment::class); + } } diff --git a/backend/app/Services/FIB/CheckPaymentStatusService.php b/backend/app/Services/FIB/CheckPaymentStatusService.php index 58218c1e..940a3520 100755 --- a/backend/app/Services/FIB/CheckPaymentStatusService.php +++ b/backend/app/Services/FIB/CheckPaymentStatusService.php @@ -11,31 +11,29 @@ class CheckPaymentStatusService { protected string $url; protected string $channelName; - protected AuthorizationService $authorizationService; + protected AuthorizationService $authorization_service; - public function __construct(AuthorizationService $authorizationService) + public function __construct(AuthorizationService $authorization_service) { - $this->url = config('fib.checkPaymentStatus.url'); + $this->url = 'https://fib-stage.fib.iq/protected/v1/payments/'; $this->channelName = 'fib_status'; - $this->authorizationService = $authorizationService; + $this->authorization_service = $authorization_service; } /** * @throws Exception */ - public function run(): array + public function run(string $fib_payment_id): array { try { - return $this->sendRequest(); - } - catch (Exception $e) { + return $this->sendRequest($fib_payment_id); + } catch (Exception $e) { Log::channel($this->channelName) ->error(get_class($this), [ 'message' => $e->getMessage(), ] ); - throw $e; } } @@ -44,15 +42,16 @@ class CheckPaymentStatusService * @throws ConnectionException * @throws Exception */ - public function sendRequest(): array + public function sendRequest(string $fib_payment_id): array { + $url = rtrim($this->url, '/') . "/{$fib_payment_id}/status"; return Http::withHeaders([ 'Authorization' => 'Bearer ' . $this->getToken(), - 'Content-Type' => 'application/json', - ]) + 'Accept' => 'application/json', + ]) ->throw() ->withoutVerifying() - ->post($this->url) + ->get($url) ->json(); } @@ -61,7 +60,7 @@ class CheckPaymentStatusService */ public function getToken(): string { - $data = $this->authorizationService->run(); + $data = $this->authorization_service->run(); return $data['access_token']; } } diff --git a/backend/app/Services/Payments/Commands/SyncFibPaymentStatuses.php b/backend/app/Services/Payments/Commands/SyncFibPaymentStatuses.php new file mode 100644 index 00000000..4db10d38 --- /dev/null +++ b/backend/app/Services/Payments/Commands/SyncFibPaymentStatuses.php @@ -0,0 +1,20 @@ +info('Syncing FIB payment statuses...'); + $service->run(); + $this->info('Done.'); + } +} diff --git a/backend/app/Services/Payments/Controllers/FIBTempController.php b/backend/app/Services/Payments/Controllers/FIBTempController.php new file mode 100644 index 00000000..5a5ce421 --- /dev/null +++ b/backend/app/Services/Payments/Controllers/FIBTempController.php @@ -0,0 +1,40 @@ +check_status_service = $check_status_service; + } + + public function checkStatus(Request $request): JsonResponse + { + try { + $result = $this->check_status_service->run('8a6e8f4d-b9fb-44f4-b086-12450939bf5c'); + return $this->successResponse($result); + } catch (\Exception $exception) { + throw $exception; + } + } + + +} diff --git a/backend/app/Services/Payments/PaymentService.php b/backend/app/Services/Payments/PaymentService.php index 8116ab6c..6b4defb4 100644 --- a/backend/app/Services/Payments/PaymentService.php +++ b/backend/app/Services/Payments/PaymentService.php @@ -50,8 +50,8 @@ class PaymentService 'amount' => $dto->amount, 'currency' => $dto->currency, 'callback_url' => 'https://user_callback', - 'status_id' => PaymentStatusEnum::Unpaid->value, - 'status_name' => PaymentStatusEnum::Unpaid->name, + 'status_id' => PaymentStatusEnum::UNPAID->value, + 'status_name' => PaymentStatusEnum::UNPAID->name, 'ip' => $dto->ip, 'user_gateway_id' => $gateway->id, ]); @@ -91,8 +91,8 @@ class PaymentService 'paymentId' => $fib_response_dto->paymentId, 'paymentMethod' => 'fib', 'validUntil' => $fib_response_dto->validUntil, - 'status_id' => PaymentStatusEnum::Unpaid->value, - 'status_name' => PaymentStatusEnum::Unpaid->name, + 'status_id' => PaymentStatusEnum::UNPAID->value, + 'status_name' => PaymentStatusEnum::UNPAID->name, ]); return [ @@ -104,7 +104,7 @@ class PaymentService 'business_app_link' => $fib_response_dto->businessAppLink, 'corporate_app_link' => $fib_response_dto->corporateAppLink, 'valid_until' => $fib_response_dto->validUntil, - 'status' => PaymentStatusEnum::Unpaid->name, + 'status' => PaymentStatusEnum::UNPAID->name, ]; }); } @@ -120,9 +120,9 @@ class PaymentService public function checkPaymentStatus($track_id) { - $user_id = auth()->id(); + $user_id = auth()->id() ?? 2; return Payment::query()->where('track_id', $track_id) - ->where('user_id', 2) + ->where('user_id', $user_id) ->firstOrFail(); } @@ -137,7 +137,7 @@ class PaymentService ->lockForUpdate() ->firstOrFail(); - if ($payment->status_id === PaymentStatusEnum::Paid->value) { + if ($payment->status_id == PaymentStatusEnum::PAID->value) { return [ 'status' => 'already_verified', 'paid' => true, @@ -152,15 +152,15 @@ class PaymentService } $payment->update([ - 'status_id' => PaymentStatusEnum::Paid->value, - 'status_name' => PaymentStatusEnum::Paid->name, + 'status_id' => PaymentStatusEnum::PAID->value, + 'status_name' => PaymentStatusEnum::PAID->name, ]); Transaction::query() ->where('payment_id', $payment->id) ->update([ - 'status_id' => PaymentStatusEnum::Paid->value, - 'status_name' => PaymentStatusEnum::Paid->name, + 'status_id' => PaymentStatusEnum::PAID->value, + 'status_name' => PaymentStatusEnum::PAID->name, ]); return [ diff --git a/backend/app/Services/Payments/SyncFibPaymentStatusService.php b/backend/app/Services/Payments/SyncFibPaymentStatusService.php new file mode 100644 index 00000000..e689ac1d --- /dev/null +++ b/backend/app/Services/Payments/SyncFibPaymentStatusService.php @@ -0,0 +1,113 @@ +whereIn('status_id', [ + PaymentStatusEnum::UNPAID->value, + ]) + ->with('transaction.payment') + ->get(); + + foreach ($fib_transactions as $fib_transaction) { + try { + $this->syncOne($fib_transaction); + } catch (\Exception $e) { + Log::channel('fib_status')->error('Failed to sync fib transaction', [ + 'fib_transaction_id' => $fib_transaction->id, + 'fib_payment_id' => $fib_transaction->paymentId, + 'error' => $e->getMessage(), + ]); + } + } + } + + public function syncOne(FibTransaction $fib_transaction) + { + + $fib_status_response = $this->checkPaymentStatusService->run($fib_transaction->paymentId); + if (!$fib_status_response['status']) { + Log::error('No status feild in fib ', []); + return false; + } + $mapped_status = $this->mapFibStatus($fib_status_response['status']); + +// if ($mapped_status->value == $fib_transaction->status) { +// return; +// } + + return DB::transaction(function () use ($fib_transaction, $fib_status_response, $mapped_status) { + $transaction = $fib_transaction->transaction; + $payment = $transaction->payment; + $fib_transaction->update([ + 'status_id' => $mapped_status->value, + 'status_name' => $fib_transaction->status, + 'paidAt' => $fib_status_response['paidAt'] ?? null, + 'paidAmount' => $fib_status_response['amount']['amount'] ?? null, + 'paidCurrency' => $fib_status_response['amount']['currency'] ?? null, + 'decliningReason' => $fib_status_response['decliningReason'] ?? null, + 'declinedAt' => $fib_status_response['declinedAt'] ?? null, + 'paidByName' => $fib_status_response['paidBy']['name'] ?? null, + 'paidByIban' => $fib_status_response['paidBy']['iban'] ?? null, + ]); + + $transaction->update([ + 'status_id' => $mapped_status->value, + 'status_name' => $mapped_status->label(), + ]); + + if ($mapped_status === PaymentStatusEnum::PAID) { + $payment->update([ + 'status_id' => PaymentStatusEnum::PendingVerification->value, + 'status_name' => PaymentStatusEnum::PendingVerification->label(), + 'payment_method' => 'FIB', + 'paid_currency' => $fib_transaction->paidCurrency, + 'paid_amount' => $fib_transaction->paidAmount, + 'paid_at' => $fib_transaction->paidAt, + ]); + #------ has to implement ------ +// $this->notifyMerchant($payment); + } else { + $payment->update([ + 'status_id' => $mapped_status->value, + 'status_name' => $mapped_status->label(), + 'paid_currency' => $fib_transaction->paidCurrency, + 'paid_amount' => $fib_transaction->paidAmount, + 'paid_at' => $fib_transaction->paidAt, + ]); + } + return $payment; + }); + } + + protected function mapFibStatus(string $fib_status_response): PaymentStatusEnum + { + return match (strtoupper($fib_status_response)) { + 'PAID' => PaymentStatusEnum::PAID, + 'UNPAID' => PaymentStatusEnum::UNPAID, + 'DECLINED' => PaymentStatusEnum::Cancelled, + 'EXPIRED' => PaymentStatusEnum::Expired, + 'REFUNDED' => PaymentStatusEnum::REFUNDED, + 'REFUND_REQUESTED' => PaymentStatusEnum::REFUND_REQUESTED, + default => PaymentStatusEnum::UNPAID, + }; + } +} diff --git a/backend/app/User/Controllers/Payment/PaymentController.php b/backend/app/User/Controllers/Payment/PaymentController.php index 6611ec51..94193580 100644 --- a/backend/app/User/Controllers/Payment/PaymentController.php +++ b/backend/app/User/Controllers/Payment/PaymentController.php @@ -36,7 +36,7 @@ class PaymentController extends Controller } } - public function checkPaymentStatus(InquiryRequest $request): JsonResponse + public function InquiryTransaction(InquiryRequest $request): JsonResponse { try { $result = $this->payment_service->checkPaymentStatus($request->track_id); diff --git a/backend/bootstrap/app.php b/backend/bootstrap/app.php index 31119ffe..61c12ea1 100755 --- a/backend/bootstrap/app.php +++ b/backend/bootstrap/app.php @@ -3,12 +3,13 @@ use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; +use Illuminate\Support\Facades\Route; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( - web: __DIR__.'/../routes/web.php', - api: __DIR__.'/../routes/api.php', - commands: __DIR__.'/../routes/console.php', + web: __DIR__ . '/../routes/web.php', + api: __DIR__ . '/../routes/api.php', + commands: __DIR__ . '/../routes/console.php', health: '/up', then: function () { Route::middleware('web') @@ -23,6 +24,9 @@ return Application::configure(basePath: dirname(__DIR__)) ->withExceptions(function (Exceptions $exceptions): void { // }) + ->withCommands([ + __DIR__ . '/../app/Services/Payments/Commands', + ]) ->withEvents(discover: [ - __DIR__.'/../app/User/Listeners', + __DIR__ . '/../app/User/Listeners', ])->create(); diff --git a/backend/database/seeders/PaymentStatusSeeder.php b/backend/database/seeders/PaymentStatusSeeder.php index 875ff488..972a9449 100644 --- a/backend/database/seeders/PaymentStatusSeeder.php +++ b/backend/database/seeders/PaymentStatusSeeder.php @@ -6,7 +6,7 @@ use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; -class PaymentStatusSeeder extends Seeder +class PaymentStatusSeeder extends Seeder { /** * Run the database seeds. @@ -14,11 +14,13 @@ class PaymentStatusSeeder extends Seeder public function run(): void { DB::table('payment_statuses')->insert([ - ['id' => 0, 'name' => 'unpaid', 'created_at' => now(), 'updated_at' => now(),], - ['id' => 1, 'name' => 'cancelled', 'created_at' => now(), 'updated_at' => now(),], - ['id' => 2, 'name' => 'expired', 'created_at' => now(), 'updated_at' => now(),], - ['id' => 3, 'name' => 'paid', 'created_at' => now(), 'updated_at' => now(),], - ['id' => 4, 'name' => 'refunded', 'created_at' => now(), 'updated_at' => now(),], + ['id' => 0, 'name' => 'UNPAID', 'created_at' => now(), 'updated_at' => now(),], + ['id' => 1, 'name' => 'CANCELLED', 'created_at' => now(), 'updated_at' => now(),], + ['id' => 2, 'name' => 'EXPIRED', 'created_at' => now(), 'updated_at' => now(),], + ['id' => 3, 'name' => 'PAID', 'created_at' => now(), 'updated_at' => now(),], + ['id' => 4, 'name' => 'REFUNDED', 'created_at' => now(), 'updated_at' => now(),], + ['id' => 5, 'name' => 'PENDING_VERIFICATION', 'created_at' => now(), 'updated_at' => now(),], + ['id' => 6, 'name' => 'REFUND_REQUESTED', 'created_at' => now(), 'updated_at' => now(),], ]); } } diff --git a/backend/routes/api.php b/backend/routes/api.php index 594f6249..ce49735a 100755 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -11,11 +11,12 @@ Route::get('/user', function (Request $request) { Route::prefix('v1')->group(function () { Route::prefix('payment') -// ->middleware('auth:api') ->group(function () { Route::post('/create_payment', [PaymentController::class, 'createPayment'])->name('create_payment'); - Route::post('/inquiry', [PaymentController::class, 'checkPaymentStatus'])->name('inquiry'); - Route::post('/verify', [PaymentController::class, 'checkPaymentStatus'])->name('inquiry'); + Route::post('/inquiry', [PaymentController::class, 'InquiryTransaction'])->name('inquiry'); + Route::post('/verify', [PaymentController::class, 'checkPaymentStatus'])->name('verify'); + Route::get('check_fib_status', [\App\Services\Payments\Controllers\FIBTempController::class, 'checkStatus']); }); - }); + + diff --git a/backend/routes/console.php b/backend/routes/console.php index 3c9adf1a..8216f640 100755 --- a/backend/routes/console.php +++ b/backend/routes/console.php @@ -2,7 +2,13 @@ use Illuminate\Foundation\Inspiring; use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Schedule; +use App\Services\Payments\Commands\SyncFibPaymentStatuses; + + Artisan::command('inspire', function () { $this->comment(Inspiring::quote()); })->purpose('Display an inspiring quote'); + +Schedule::command(SyncFibPaymentStatuses::class)->everyMinute();