fix(payment): unify and harden payment attempts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-07-27 11:29:57 +08:00
1 parent 53933988bf
commit 089421cd3e
18 files changed
+680 -476

No files matched your search

+22 -74
View File
@@ -328,88 +328,36 @@ class UserOrders {
return ['success' => false, 'message' => __('该支付方式尚未配置,请联系站长。', 'aksrt')];
}
$lock = class_exists(PaymentAttemptLock::class)
? PaymentAttemptLock::acquire($order_number)
: false;
if ($lock === false) {
return ['success' => false, 'message' => __('该订单正在发起支付,请稍后重试。', 'aksrt')];
$order['subject'] = $order['subject'] ?? sprintf(__('订单 %s', 'aksrt'), $order_number);
$order['payment_method'] = $payment_method;
$payment_result = PaymentOrchestrator::pay($gateway, $order, [
'paid_note' => __('用户在前台完成支付。', 'aksrt'),
]);
if (empty($payment_result['success'])) {
return ['success' => false, 'message' => $payment_result['message']];
}
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')];
}
$code_url = $payment_result['payment_data']['code_url'] ?? '';
if (!empty($payment_result['payment_url']) || $code_url !== '') {
return [
'success' => true,
'data' => [
'redirect' => true,
'payment_url' => (string) ($payment_result['payment_url'] ?? ''),
'qr_content' => (string) $code_url,
'order_number' => $order_number,
'message' => $payment_result['message'] ?? __('请在新页面完成支付。', 'aksrt'),
],
];
}
$paid = $repo->update_status($order_number, Order::STATUS_PAID, [
'payment_method' => $payment_method,
'payment_transaction_id' => sanitize_text_field((string) ($payment_result['transaction_id'] ?? '')),
'note' => __('用户在前台完成支付。', 'aksrt'),
'require_transition' => true,
]);
if (!$paid) {
$compensation = $gateway->refund(
$order_number,
(float) ($order['amount'] ?? 0),
'PAY_ROLLBACK_' . substr(hash('sha256', $order_number), 0, 24)
);
if (empty($compensation['success'])) {
error_log(sprintf(
'[AKSRT Payment] rollback failed order=%s gateway=%s: %s',
$order_number,
$payment_method,
(string) ($compensation['message'] ?? 'unknown error')
));
return ['success' => false, 'message' => __('订单状态已变化,扣款补偿失败,请联系管理员处理。', 'aksrt')];
}
return ['success' => false, 'message' => __('订单状态已变化,扣款已退回。', 'aksrt')];
}
if (!empty($payment_result['redirect'])) {
return [
'success' => true,
'data' => [
'order' => $this->get_order_detail_view($user_id, $order_number),
'message' => $payment_result['message'] ?? __('支付成功。', 'aksrt'),
'redirect' => true,
'payment_url' => (string) ($payment_result['payment_url'] ?? ''),
'qr_content' => (string) ($payment_result['qr_content'] ?? ''),
'order_number' => $order_number,
'message' => $payment_result['message'],
],
];
} finally {
PaymentAttemptLock::release($order_number, $lock);
}
return [
'success' => true,
'data' => [
'order' => $this->get_order_detail_view($user_id, $order_number),
'message' => $payment_result['message'],
],
];
}
private function empty_stats(): array {