fix: improve h5 payment return flow

This commit is contained in:
wushumin
2026-06-04 16:12:59 +08:00
parent 13c21ac67f
commit 46dae160be
13 changed files with 328 additions and 8 deletions

View File

@@ -8,9 +8,9 @@ PUBLIC_FILE_BASE_URL=
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=anxinyan
DB_USERNAME=root
DB_PASSWORD=change_me
DB_DATABASE=test_anxinyan
DB_USERNAME=
DB_PASSWORD=
DB_CHARSET=utf8mb4
DB_PREFIX=

View File

@@ -88,7 +88,11 @@ class ShouqianbaConfigService
return '';
}
return $baseUrl . '/#/pages/order/detail?id=' . $orderId;
$fallbackQuery = http_build_query([
'sqb_return_order_id' => $orderId,
], '', '&', PHP_QUERY_RFC3986);
return $baseUrl . '/?' . $fallbackQuery . '#/pages/order/detail?id=' . $orderId;
}
public function miniProgramCallbackPath(int $orderId): string

View File

@@ -54,6 +54,11 @@ class ShouqianbaPaymentService
$latest = $this->latestPayment($orderId);
if ($latest && in_array((string)$latest['status'], ['pending', 'created'], true) && (string)$latest['order_token'] !== '') {
if ($this->shouldRefreshH5ReturnUrl($latest, $order)) {
$replacement = $this->createPayment($order);
$this->markPaymentReplaced($latest);
return $replacement;
}
return $this->buildPaymentLaunchPayload($latest, $order);
}
@@ -281,6 +286,44 @@ class ShouqianbaPaymentService
return $payload;
}
private function shouldRefreshH5ReturnUrl(array $payment, array $order): bool
{
if ((string)$order['source_channel'] !== 'h5') {
return false;
}
$expectedUrl = $this->configService->h5OrderDetailUrl((int)$order['id']);
if ($expectedUrl === '') {
return false;
}
$requestJson = json_decode((string)($payment['request_json'] ?? ''), true);
if (!is_array($requestJson)) {
return true;
}
$body = $requestJson['body'] ?? ($requestJson['request']['body'] ?? null);
if (!is_array($body)) {
return true;
}
return (string)($body['return_url'] ?? '') !== $expectedUrl
|| (string)($body['back_url'] ?? '') !== $expectedUrl;
}
private function markPaymentReplaced(array $payment): void
{
$now = date('Y-m-d H:i:s');
Db::name('shouqianba_payments')
->where('id', (int)$payment['id'])
->whereIn('status', ['pending', 'created'])
->update([
'status' => 'replaced',
'cancelled_at' => $now,
'updated_at' => $now,
]);
}
private function queryRemotePayment(array $payment): array
{
$config = $this->configService->assertReady(true);

View File

@@ -320,6 +320,32 @@ try {
assertTrue(($launch['status'] ?? '') === 'pending', 'purchase should create pending payment');
assertTrue(($launch['cashier_url'] ?? '') !== '', 'purchase cashier_url missing');
$payment = latestPayment($notifyOrderId);
$requestJson = json_decode((string)$payment['request_json'], true);
$purchaseBody = is_array($requestJson) ? ($requestJson['body'] ?? []) : [];
assertTrue(
($purchaseBody['return_url'] ?? '') === 'https://m.example.com/?sqb_return_order_id=' . $notifyOrderId . '#/pages/order/detail?id=' . $notifyOrderId,
'purchase return_url should include H5 order detail fallback'
);
assertTrue(($purchaseBody['back_url'] ?? '') === ($purchaseBody['return_url'] ?? ''), 'purchase back_url should match return_url');
$staleReturnOrderId = createMockOrder($userId, 'STALERETURN');
$service->createOrReusePayment($staleReturnOrderId);
$stalePayment = latestPayment($staleReturnOrderId);
$staleRequestJson = json_decode((string)$stalePayment['request_json'], true);
if (is_array($staleRequestJson)) {
$staleRequestJson['body']['return_url'] = 'https://m.example.com/#/pages/order/detail?id=' . $staleReturnOrderId;
$staleRequestJson['body']['back_url'] = $staleRequestJson['body']['return_url'];
Db::name('shouqianba_payments')->where('id', (int)$stalePayment['id'])->update([
'request_json' => json_encode($staleRequestJson, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);
}
$replacement = $service->createOrReusePayment($staleReturnOrderId);
$stalePaymentAfterReplace = Db::name('shouqianba_payments')->where('id', (int)$stalePayment['id'])->find();
$latestReplacement = latestPayment($staleReturnOrderId);
assertTrue((string)($stalePaymentAfterReplace['status'] ?? '') === 'replaced', 'stale H5 payment should be marked replaced');
assertTrue((int)$latestReplacement['id'] !== (int)$stalePayment['id'], 'stale H5 payment should be replaced with a new payment row');
assertTrue(($replacement['check_sn'] ?? '') === (string)$latestReplacement['check_sn'], 'replacement launch payload should use the new payment row');
$notifyPayload = [
'check_sn' => $payment['check_sn'],
'order_status' => '4',