From 532f2ddf99da1149b690f264cd61c2c701a9de5a Mon Sep 17 00:00:00 2001 From: Nyan Lin Paing <117423022+LinPaing21@users.noreply.github.com> Date: Tue, 18 Aug 2026 11:54:34 +0700 Subject: [PATCH] fix kbz payment success payload --- .../src/Gateways/KbzMiniAppGateway.php | 37 +++++++++++-------- .../Http/Controllers/PaymentController.php | 10 +++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/app-modules/payment/src/Gateways/KbzMiniAppGateway.php b/app-modules/payment/src/Gateways/KbzMiniAppGateway.php index 0d3282a..90ea0cd 100644 --- a/app-modules/payment/src/Gateways/KbzMiniAppGateway.php +++ b/app-modules/payment/src/Gateways/KbzMiniAppGateway.php @@ -114,13 +114,19 @@ class KbzMiniAppGateway implements PaymentGatewayInterface ); } + $orderInfo = $this->createOrderInfo($body['prepay_id'] ?? ''); + return new PaymentResultData( // KBZ's queryorder/refund calls both key off our own merch_order_id, // not their prepay_id โ€” so that's what gets stored/passed forward as // the gateway transaction id (prepay_id still lives in the payload). status: PaymentStatus::Pending, gatewayTransactionId: $data->merchantOrderId, - gatewayPayload: $body, + gatewayPayload: [ + 'prepayId' => $body['prepay_id'] ?? null, + 'orderInfo' => KbzSignature::joinKeyVal($orderInfo), + 'signature' => KbzSignature::sign($orderInfo, $this->merchantKey), + ], ); } @@ -239,7 +245,7 @@ class KbzMiniAppGateway implements PaymentGatewayInterface 'timestamp' => (string) now()->timestamp, 'method' => 'kbz.payment.precreate', 'notify_url' => $data->notifyUrl ?? $this->notifyUrl, - 'nonce_str' => $this->nonceStr(), + 'nonce_str' => uniqid(), 'version' => '1.0', 'biz_content' => [ 'appid' => $this->appId, @@ -266,7 +272,7 @@ class KbzMiniAppGateway implements PaymentGatewayInterface $params = [ 'timestamp' => (string) now()->timestamp, 'method' => 'kbz.payment.queryorder', - 'nonce_str' => $this->nonceStr(), + 'nonce_str' => uniqid(), 'version' => '1.0', 'biz_content' => [ 'appid' => $this->appId, @@ -303,7 +309,7 @@ class KbzMiniAppGateway implements PaymentGatewayInterface $params = [ 'timestamp' => (string) now()->timestamp, 'method' => 'kbz.payment.refund', - 'nonce_str' => $this->nonceStr(), + 'nonce_str' => uniqid(), 'version' => '1.0', 'biz_content' => [ 'appid' => $this->appId, @@ -329,18 +335,6 @@ class KbzMiniAppGateway implements PaymentGatewayInterface return now()->format('YmdHi').strtoupper(Str::random(8)); } - /** - * KBZ requires `nonce_str` to be a plain alphanumeric string of at most - * 32 characters โ€” no hyphens or other special characters (confirmed - * against KBZ's "Query Order" field spec). `Str::uuid()` violates both - * constraints (36 chars, hyphenated), which silently broke `precreate` - * downstream even though the request's own signature still validated. - */ - private function nonceStr(): string - { - return strtoupper(Str::random(32)); - } - /** * mTLS options for the refund call โ€” KBZ requires a client cert/key + * CA bundle on `kbz.payment.refund` specifically (domain.md ยง6). @@ -369,4 +363,15 @@ class KbzMiniAppGateway implements PaymentGatewayInterface return $options; } + + public function createOrderInfo($prepayId): array + { + return [ + 'appid' => $this->appId, + 'merch_code' => $this->merchantCode, + 'nonce_str' => uniqid(), + 'prepay_id' => $prepayId, + 'timestamp' => (string)time() + ]; + } } diff --git a/app-modules/payment/src/Http/Controllers/PaymentController.php b/app-modules/payment/src/Http/Controllers/PaymentController.php index bf4420c..e229520 100644 --- a/app-modules/payment/src/Http/Controllers/PaymentController.php +++ b/app-modules/payment/src/Http/Controllers/PaymentController.php @@ -8,6 +8,7 @@ use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Gate; use Modules\Booking\Models\Booking; use Modules\Payment\Actions\InitiatePaymentAction; +use Modules\Payment\Enums\PaymentStatus; use Modules\Payment\Http\Resources\PaymentResource; class PaymentController extends Controller @@ -26,6 +27,15 @@ class PaymentController extends Controller $payment = $this->initiatePaymentAction->handle($booking); + if ($payment->status === PaymentStatus::Failed) { + return response()->json([ + 'message' => 'Payment initiation failed', + 'errors' => [ + 'payment' => ['Payment initiation failed'], + ], + ], 422); + } + return (new PaymentResource($payment)) ->response() ->setStatusCode(201);