fix(payment): harden payment and refund chains
This commit is contained in:
1 parent
c70fbe9835
commit
53ff99f343
24 files changed
+993
-251
No files matched your search
@@ -184,8 +184,21 @@ class CheckoutService {
|
||||
return ['success' => false, 'message' => __('订单不存在。', 'aksrt')];
|
||||
}
|
||||
|
||||
// 未显式指定时,使用订单创建时选定的支付方式。
|
||||
$payment_method = sanitize_key($payment_method ?: (string) ($order['payment_method'] ?? 'balance'));
|
||||
$status = (string) ($order['status'] ?? '');
|
||||
if (!in_array($status, [
|
||||
\AKSRT\Payment\Order::STATUS_CREATED,
|
||||
\AKSRT\Payment\Order::STATUS_PENDING,
|
||||
], true)) {
|
||||
return ['success' => false, 'message' => __('当前订单状态不可支付。', 'aksrt')];
|
||||
}
|
||||
|
||||
// Payment attempts are bound to the method selected when the order was created.
|
||||
$stored_method = sanitize_key((string) ($order['payment_method'] ?? ''));
|
||||
$payment_method = sanitize_key($payment_method ?: ($stored_method ?: 'balance'));
|
||||
if ($stored_method !== '' && $payment_method !== $stored_method) {
|
||||
return ['success' => false, 'message' => __('支付方式与订单不一致,请重新下单。', 'aksrt')];
|
||||
}
|
||||
|
||||
$gateway_manager = class_exists('\AKSRT\Payment\Gateway') ? \AKSRT\Payment\Gateway::get_instance() : null;
|
||||
$gateway = $gateway_manager ? $gateway_manager->get_gateway($payment_method) : null;
|
||||
|
||||
@@ -197,8 +210,16 @@ class CheckoutService {
|
||||
return ['success' => false, 'message' => __('该支付方式尚未配置,请联系站长。', 'aksrt')];
|
||||
}
|
||||
|
||||
// 携带订单号与摘要,供网关生成支付单。
|
||||
$order['subject'] = $order['subject'] ?? sprintf(__('订单 %s', 'aksrt'), $order_number);
|
||||
$lock = class_exists('\AKSRT\Payment\PaymentAttemptLock')
|
||||
? \AKSRT\Payment\PaymentAttemptLock::acquire($order_number)
|
||||
: false;
|
||||
if ($lock === false) {
|
||||
return ['success' => false, 'message' => __('该订单正在发起支付,请稍后重试。', 'aksrt')];
|
||||
}
|
||||
|
||||
try {
|
||||
// 携带订单号与摘要,供网关生成支付单。
|
||||
$order['subject'] = $order['subject'] ?? sprintf(__('订单 %s', 'aksrt'), $order_number);
|
||||
|
||||
// Points payment: synchronous deduction via \AKSRT\Credits\Credits
|
||||
if ($payment_method === 'points') {
|
||||
@@ -227,11 +248,29 @@ class CheckoutService {
|
||||
}
|
||||
|
||||
// 同步型网关(站内余额):create_payment 已实际扣款,标记已支付(触发 aksrt_order_paid 履约)。
|
||||
$repo->update_status($order_number, \AKSRT\Payment\Order::STATUS_PAID, [
|
||||
$paid = $repo->update_status($order_number, \AKSRT\Payment\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')];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
@@ -240,6 +279,9 @@ class CheckoutService {
|
||||
'message' => $payment_result['message'] ?? __('支付成功。', 'aksrt'),
|
||||
],
|
||||
];
|
||||
} finally {
|
||||
\AKSRT\Payment\PaymentAttemptLock::release($order_number, $lock);
|
||||
}
|
||||
}
|
||||
|
||||
private function pay_with_points(int $user_id, string $order_number, array $order): array {
|
||||
@@ -269,11 +311,30 @@ class CheckoutService {
|
||||
}
|
||||
|
||||
$repo = \AKSRT\Payment\OrderRepository::get_instance();
|
||||
$repo->update_status($order_number, \AKSRT\Payment\Order::STATUS_PAID, [
|
||||
$paid = $repo->update_status($order_number, \AKSRT\Payment\Order::STATUS_PAID, [
|
||||
'payment_method' => 'points',
|
||||
'payment_transaction_id' => 'points_' . $order_number,
|
||||
'note' => sprintf(__('积分支付 %d 积分。', 'aksrt'), $amount),
|
||||
'require_transition' => true,
|
||||
]);
|
||||
if (!$paid) {
|
||||
$compensation = \AKSRT\Credits\Credits::add(
|
||||
$user_id,
|
||||
$amount,
|
||||
'shop_points_pay_rollback',
|
||||
['order' => $order_number],
|
||||
sprintf(__('订单 %s 支付失败退回积分', 'aksrt'), $order_number)
|
||||
);
|
||||
if (is_wp_error($compensation)) {
|
||||
error_log(sprintf(
|
||||
'[AKSRT Payment] points rollback failed order=%s: %s',
|
||||
$order_number,
|
||||
$compensation->get_error_message()
|
||||
));
|
||||
return ['success' => false, 'message' => __('订单状态已变化,积分补偿失败,请联系管理员处理。', 'aksrt')];
|
||||
}
|
||||
return ['success' => false, 'message' => __('订单状态已变化,积分已退回。', 'aksrt')];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
|
||||
Reference in new issue
Block a user