fix(payment): re-pay with fresh provider out_trade_no

WeChat/Alipay/Epay reject reusing the same merchant order number when
params change. Allocate attempt-suffixed out_trade_no on resume pay,
map callbacks back to the local order, and best-effort close prior trades.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-07-26 13:28:45 +08:00
1 parent 723dd180e4
commit ed55d302e9
7 files changed
+322 -24

No files matched your search

+20
View File
@@ -337,6 +337,26 @@ class UserOrders {
try {
$order['subject'] = $order['subject'] ?? sprintf(__('订单 %s', 'aksrt'), $order_number);
$needs_attempt = !in_array($payment_method, ['balance', 'points'], true);
if ($needs_attempt) {
$attempt = $repo->allocate_payment_attempt($order_number);
if ($attempt === null) {
return ['success' => false, 'message' => __('无法生成支付单号,请稍后重试。', 'aksrt')];
}
$order = $repo->get_user_order($user_id, $order_number) ?: $order;
$order['out_trade_no'] = (string) $attempt['out_trade_no'];
if (!empty($attempt['previous'])
&& $attempt['previous'] !== $attempt['out_trade_no']
&& method_exists($gateway, 'close_trade')) {
try {
$gateway->close_trade((string) $attempt['previous']);
} catch (\Throwable $e) {
// best-effort
}
}
}
$payment_result = $gateway->create_payment($order);
if (empty($payment_result['success'])) {
return ['success' => false, 'message' => $payment_result['message'] ?? __('支付失败。', 'aksrt')];