This commit is contained in:
@@ -114,13 +114,19 @@ class KbzMiniAppGateway implements PaymentGatewayInterface
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$orderInfo = $this->createOrderInfo($body['prepay_id'] ?? '');
|
||||||
|
|
||||||
return new PaymentResultData(
|
return new PaymentResultData(
|
||||||
// KBZ's queryorder/refund calls both key off our own merch_order_id,
|
// 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
|
// not their prepay_id — so that's what gets stored/passed forward as
|
||||||
// the gateway transaction id (prepay_id still lives in the payload).
|
// the gateway transaction id (prepay_id still lives in the payload).
|
||||||
status: PaymentStatus::Pending,
|
status: PaymentStatus::Pending,
|
||||||
gatewayTransactionId: $data->merchantOrderId,
|
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,
|
'timestamp' => (string) now()->timestamp,
|
||||||
'method' => 'kbz.payment.precreate',
|
'method' => 'kbz.payment.precreate',
|
||||||
'notify_url' => $data->notifyUrl ?? $this->notifyUrl,
|
'notify_url' => $data->notifyUrl ?? $this->notifyUrl,
|
||||||
'nonce_str' => $this->nonceStr(),
|
'nonce_str' => uniqid(),
|
||||||
'version' => '1.0',
|
'version' => '1.0',
|
||||||
'biz_content' => [
|
'biz_content' => [
|
||||||
'appid' => $this->appId,
|
'appid' => $this->appId,
|
||||||
@@ -266,7 +272,7 @@ class KbzMiniAppGateway implements PaymentGatewayInterface
|
|||||||
$params = [
|
$params = [
|
||||||
'timestamp' => (string) now()->timestamp,
|
'timestamp' => (string) now()->timestamp,
|
||||||
'method' => 'kbz.payment.queryorder',
|
'method' => 'kbz.payment.queryorder',
|
||||||
'nonce_str' => $this->nonceStr(),
|
'nonce_str' => uniqid(),
|
||||||
'version' => '1.0',
|
'version' => '1.0',
|
||||||
'biz_content' => [
|
'biz_content' => [
|
||||||
'appid' => $this->appId,
|
'appid' => $this->appId,
|
||||||
@@ -303,7 +309,7 @@ class KbzMiniAppGateway implements PaymentGatewayInterface
|
|||||||
$params = [
|
$params = [
|
||||||
'timestamp' => (string) now()->timestamp,
|
'timestamp' => (string) now()->timestamp,
|
||||||
'method' => 'kbz.payment.refund',
|
'method' => 'kbz.payment.refund',
|
||||||
'nonce_str' => $this->nonceStr(),
|
'nonce_str' => uniqid(),
|
||||||
'version' => '1.0',
|
'version' => '1.0',
|
||||||
'biz_content' => [
|
'biz_content' => [
|
||||||
'appid' => $this->appId,
|
'appid' => $this->appId,
|
||||||
@@ -329,18 +335,6 @@ class KbzMiniAppGateway implements PaymentGatewayInterface
|
|||||||
return now()->format('YmdHi').strtoupper(Str::random(8));
|
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 +
|
* mTLS options for the refund call — KBZ requires a client cert/key +
|
||||||
* CA bundle on `kbz.payment.refund` specifically (domain.md §6).
|
* CA bundle on `kbz.payment.refund` specifically (domain.md §6).
|
||||||
@@ -369,4 +363,15 @@ class KbzMiniAppGateway implements PaymentGatewayInterface
|
|||||||
|
|
||||||
return $options;
|
return $options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createOrderInfo($prepayId): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'appid' => $this->appId,
|
||||||
|
'merch_code' => $this->merchantCode,
|
||||||
|
'nonce_str' => uniqid(),
|
||||||
|
'prepay_id' => $prepayId,
|
||||||
|
'timestamp' => (string)time()
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Illuminate\Routing\Controller;
|
|||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Modules\Booking\Models\Booking;
|
use Modules\Booking\Models\Booking;
|
||||||
use Modules\Payment\Actions\InitiatePaymentAction;
|
use Modules\Payment\Actions\InitiatePaymentAction;
|
||||||
|
use Modules\Payment\Enums\PaymentStatus;
|
||||||
use Modules\Payment\Http\Resources\PaymentResource;
|
use Modules\Payment\Http\Resources\PaymentResource;
|
||||||
|
|
||||||
class PaymentController extends Controller
|
class PaymentController extends Controller
|
||||||
@@ -26,6 +27,15 @@ class PaymentController extends Controller
|
|||||||
|
|
||||||
$payment = $this->initiatePaymentAction->handle($booking);
|
$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))
|
return (new PaymentResource($payment))
|
||||||
->response()
|
->response()
|
||||||
->setStatusCode(201);
|
->setStatusCode(201);
|
||||||
|
|||||||
Reference in New Issue
Block a user