fix(payment): unify and harden payment attempts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
53933988bf
commit
089421cd3e
18 files changed
+680
-476
No files matched your search
@@ -209,37 +209,49 @@ trait PayOrders {
|
||||
return new \WP_Error('aksrt_order_create_failed', __('创建订单失败。', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
|
||||
$deducted = \AKSRT\Credits\Credits::deduct(
|
||||
$user_id,
|
||||
$amount,
|
||||
'content_points_pay',
|
||||
['type' => 'post', 'id' => $post_id, 'order' => $order_number],
|
||||
sprintf(__('积分购买《%s》', 'aksrt'), get_the_title($post_id))
|
||||
);
|
||||
|
||||
if (is_wp_error($deducted) || $deducted === false) {
|
||||
\AKSRT\Payment\Order::get_instance()->update_status($order_number, \AKSRT\Payment\Order::STATUS_CANCELLED, [
|
||||
'note' => __('积分扣除失败,自动取消', 'aksrt'),
|
||||
]);
|
||||
$msg = is_wp_error($deducted) ? $deducted->get_error_message() : __('积分扣除失败。', 'aksrt');
|
||||
return new \WP_Error('aksrt_credits_deduct_failed', $msg ?: __('积分扣除失败。', 'aksrt'), ['status' => 400]);
|
||||
$lock = class_exists('\AKSRT\Payment\PaymentAttemptLock')
|
||||
? \AKSRT\Payment\PaymentAttemptLock::acquire($order_number)
|
||||
: false;
|
||||
if ($lock === false) {
|
||||
return new \WP_Error('aksrt_pay_locked', __('该订单正在发起支付,请稍后重试。', 'aksrt'), ['status' => 409]);
|
||||
}
|
||||
|
||||
$paid = \AKSRT\Payment\Order::get_instance()->update_status($order_number, \AKSRT\Payment\Order::STATUS_PAID, [
|
||||
'payment_method' => 'points',
|
||||
'payment_transaction_id' => 'points_' . $order_number,
|
||||
'note' => sprintf(__('积分支付 %d 积分。', 'aksrt'), $amount),
|
||||
]);
|
||||
|
||||
if (!$paid) {
|
||||
\AKSRT\Credits\Credits::add(
|
||||
try {
|
||||
$deducted = \AKSRT\Credits\Credits::deduct(
|
||||
$user_id,
|
||||
$amount,
|
||||
'content_points_pay_refund',
|
||||
'content_points_pay',
|
||||
['type' => 'post', 'id' => $post_id, 'order' => $order_number],
|
||||
sprintf(__('购买失败退款:%s', 'aksrt'), get_the_title($post_id))
|
||||
sprintf(__('积分购买《%s》', 'aksrt'), get_the_title($post_id))
|
||||
);
|
||||
return new \WP_Error('aksrt_order_confirm_failed', __('订单确认失败,已尝试退回积分。', 'aksrt'), ['status' => 500]);
|
||||
|
||||
if (is_wp_error($deducted) || $deducted === false) {
|
||||
\AKSRT\Payment\Order::get_instance()->update_status($order_number, \AKSRT\Payment\Order::STATUS_CANCELLED, [
|
||||
'note' => __('积分扣除失败,自动取消', 'aksrt'),
|
||||
]);
|
||||
$msg = is_wp_error($deducted) ? $deducted->get_error_message() : __('积分扣除失败。', 'aksrt');
|
||||
return new \WP_Error('aksrt_credits_deduct_failed', $msg ?: __('积分扣除失败。', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
|
||||
$paid = \AKSRT\Payment\Order::get_instance()->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) {
|
||||
\AKSRT\Credits\Credits::add(
|
||||
$user_id,
|
||||
$amount,
|
||||
'content_points_pay_refund',
|
||||
['type' => 'post', 'id' => $post_id, 'order' => $order_number],
|
||||
sprintf(__('购买失败退款:%s', 'aksrt'), get_the_title($post_id))
|
||||
);
|
||||
return new \WP_Error('aksrt_order_confirm_failed', __('订单确认失败,已尝试退回积分。', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
} finally {
|
||||
\AKSRT\Payment\PaymentAttemptLock::release($order_number, $lock);
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -453,69 +465,50 @@ trait PayOrders {
|
||||
return new \WP_Error('aksrt_pay_unavailable', __('支付网关不可用。', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
|
||||
$gateway_manager = \AKSRT\Payment\Gateway::get_instance();
|
||||
$gateway = $gateway_manager->get_gateway($payment_method);
|
||||
if (!$gateway) {
|
||||
return new \WP_Error('aksrt_gateway_unavailable', __('当前支付方式不可用。', 'aksrt'), [
|
||||
'status' => 400,
|
||||
'order_number' => $order_number,
|
||||
]);
|
||||
}
|
||||
if (method_exists($gateway, 'is_configured') && !$gateway->is_configured()) {
|
||||
return new \WP_Error('aksrt_gateway_unconfigured', __('该支付方式尚未配置,请联系站长。', 'aksrt'), [
|
||||
'status' => 400,
|
||||
'order_number' => $order_number,
|
||||
]);
|
||||
$resolved = \AKSRT\Payment\PaymentOrchestrator::resolve_gateway($payment_method);
|
||||
if (empty($resolved['gateway'])) {
|
||||
return new \WP_Error(
|
||||
(string) ($resolved['error_code'] ?? 'aksrt_gateway_unavailable'),
|
||||
(string) ($resolved['message'] ?? __('当前支付方式不可用。', 'aksrt')),
|
||||
['status' => 400, 'order_number' => $order_number]
|
||||
);
|
||||
}
|
||||
|
||||
$payment_result = $gateway->create_payment($order);
|
||||
$payment_result = \AKSRT\Payment\PaymentOrchestrator::pay($resolved['gateway'], $order, [
|
||||
'paid_note' => __('用户完成内容购买支付。', 'aksrt'),
|
||||
]);
|
||||
if (empty($payment_result['success'])) {
|
||||
$msg = (string) ($payment_result['message'] ?? __('支付失败。', 'aksrt'));
|
||||
$code = ($payment_method === 'balance' && (str_contains($msg, '余额') || str_contains($msg, '不足')))
|
||||
? 'aksrt_balance_insufficient'
|
||||
: 'aksrt_pay_failed';
|
||||
return new \WP_Error($code, $msg, [
|
||||
'status' => 402,
|
||||
'order_number' => $order_number,
|
||||
]);
|
||||
return new \WP_Error(
|
||||
(string) ($payment_result['code'] ?? 'aksrt_pay_failed'),
|
||||
(string) ($payment_result['message'] ?? __('支付失败。', 'aksrt')),
|
||||
['status' => 402, 'order_number' => $order_number]
|
||||
);
|
||||
}
|
||||
|
||||
$code_url = (string) ($payment_result['payment_data']['code_url'] ?? '');
|
||||
$payment_url = (string) ($payment_result['payment_url'] ?? '');
|
||||
if ($payment_url !== '' || $code_url !== '') {
|
||||
if (!empty($payment_result['redirect'])) {
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'redirect' => true,
|
||||
'payment_url' => $payment_url,
|
||||
'qr_content' => $code_url,
|
||||
'payment_url' => (string) ($payment_result['payment_url'] ?? ''),
|
||||
'qr_content' => (string) ($payment_result['qr_content'] ?? ''),
|
||||
'order_number' => $order_number,
|
||||
'amount' => $amount,
|
||||
'pay_modo' => '0',
|
||||
'message' => $payment_result['message'] ?? __('请在新页面完成支付。', 'aksrt'),
|
||||
'message' => $payment_result['message'],
|
||||
'reload' => false,
|
||||
'poll' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// Sync gateway (balance): mark paid.
|
||||
$paid = \AKSRT\Payment\Order::get_instance()->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'),
|
||||
]);
|
||||
if (!$paid) {
|
||||
return new \WP_Error('aksrt_order_confirm_failed', __('订单确认失败。', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'order_number' => $order_number,
|
||||
'amount' => $amount,
|
||||
'pay_modo' => '0',
|
||||
'message' => $payment_result['message'] ?? __('支付成功', 'aksrt'),
|
||||
'message' => $payment_result['message'],
|
||||
'reload' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -514,7 +514,7 @@ trait VipServicePackages {
|
||||
return $methods;
|
||||
}
|
||||
$gw = \AKSRT\Payment\Gateway::get_instance();
|
||||
foreach (['balance', 'alipay', 'wechatpay', 'paypal', 'stripe'] as $id) {
|
||||
foreach (['balance', 'alipay', 'wechatpay', 'paypal', 'stripe', 'epay_alipay', 'epay_wxpay'] as $id) {
|
||||
$g = $gw->get_gateway($id);
|
||||
if (!$g) {
|
||||
continue;
|
||||
|
||||
@@ -344,59 +344,42 @@ trait VipServicePurchase {
|
||||
if (!class_exists('\AKSRT\Payment\Gateway')) {
|
||||
return new \WP_Error('aksrt_pay_unavailable', __('支付网关不可用', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
$gateway = \AKSRT\Payment\Gateway::get_instance()->get_gateway($payment_method);
|
||||
if (!$gateway) {
|
||||
return new \WP_Error('aksrt_gateway_unavailable', __('当前支付方式不可用', 'aksrt'), [
|
||||
'status' => 400,
|
||||
'order_number' => $order_number,
|
||||
]);
|
||||
}
|
||||
if (method_exists($gateway, 'is_configured') && !$gateway->is_configured()) {
|
||||
return new \WP_Error('aksrt_gateway_unconfigured', __('该支付方式尚未配置,请联系站长。', 'aksrt'), [
|
||||
'status' => 400,
|
||||
'order_number' => $order_number,
|
||||
]);
|
||||
$resolved = \AKSRT\Payment\PaymentOrchestrator::resolve_gateway($payment_method);
|
||||
if (empty($resolved['gateway'])) {
|
||||
return new \WP_Error(
|
||||
(string) ($resolved['error_code'] ?? 'aksrt_gateway_unavailable'),
|
||||
(string) ($resolved['message'] ?? __('当前支付方式不可用', 'aksrt')),
|
||||
['status' => 400, 'order_number' => $order_number]
|
||||
);
|
||||
}
|
||||
|
||||
$payment_result = $gateway->create_payment($order);
|
||||
$payment_result = \AKSRT\Payment\PaymentOrchestrator::pay($resolved['gateway'], $order, [
|
||||
'paid_note' => __('用户完成会员开通支付。', 'aksrt'),
|
||||
]);
|
||||
if (empty($payment_result['success'])) {
|
||||
$msg = (string) ($payment_result['message'] ?? __('支付失败', 'aksrt'));
|
||||
$code = ($payment_method === 'balance' && (str_contains($msg, '余额') || str_contains($msg, '不足')))
|
||||
? 'aksrt_balance_insufficient'
|
||||
: 'aksrt_pay_failed';
|
||||
return new \WP_Error($code, $msg, [
|
||||
'status' => 402,
|
||||
'order_number' => $order_number,
|
||||
]);
|
||||
return new \WP_Error(
|
||||
(string) ($payment_result['code'] ?? 'aksrt_pay_failed'),
|
||||
(string) ($payment_result['message'] ?? __('支付失败', 'aksrt')),
|
||||
['status' => 402, 'order_number' => $order_number]
|
||||
);
|
||||
}
|
||||
|
||||
$code_url = (string) ($payment_result['payment_data']['code_url'] ?? '');
|
||||
$payment_url = (string) ($payment_result['payment_url'] ?? '');
|
||||
if ($payment_url !== '' || $code_url !== '') {
|
||||
if (!empty($payment_result['redirect'])) {
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'redirect' => true,
|
||||
'payment_url' => $payment_url,
|
||||
'qr_content' => $code_url,
|
||||
'payment_url' => (string) ($payment_result['payment_url'] ?? ''),
|
||||
'qr_content' => (string) ($payment_result['qr_content'] ?? ''),
|
||||
'order_number' => $order_number,
|
||||
'amount' => $amount,
|
||||
'message' => $payment_result['message'] ?? __('请在新页面完成支付。', 'aksrt'),
|
||||
'message' => $payment_result['message'],
|
||||
'reload' => false,
|
||||
'poll' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$paid = \AKSRT\Payment\Order::get_instance()->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'),
|
||||
]);
|
||||
if (!$paid) {
|
||||
return new \WP_Error('aksrt_order_confirm_failed', __('订单确认失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
@@ -637,35 +620,47 @@ trait VipServicePurchase {
|
||||
return new \WP_Error('aksrt_order_create_failed', __('创建订单失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
|
||||
$deducted = \AKSRT\Credits\Credits::deduct(
|
||||
$user_id,
|
||||
$cost,
|
||||
'vip_points_exchange',
|
||||
['order' => $order_number, 'level' => $level, 'days' => $days],
|
||||
sprintf(__('积分兑换会员 · %s', 'aksrt'), (string) ($match['title'] ?? ''))
|
||||
);
|
||||
if (is_wp_error($deducted) || $deducted === false) {
|
||||
\AKSRT\Payment\Order::get_instance()->update_status($order_number, \AKSRT\Payment\Order::STATUS_CANCELLED, [
|
||||
'note' => __('积分扣除失败,自动取消', 'aksrt'),
|
||||
]);
|
||||
$msg = is_wp_error($deducted) ? $deducted->get_error_message() : __('积分扣除失败', 'aksrt');
|
||||
return new \WP_Error('aksrt_credits_deduct_failed', $msg ?: __('积分扣除失败', 'aksrt'), ['status' => 400]);
|
||||
$lock = class_exists('\AKSRT\Payment\PaymentAttemptLock')
|
||||
? \AKSRT\Payment\PaymentAttemptLock::acquire($order_number)
|
||||
: false;
|
||||
if ($lock === false) {
|
||||
return new \WP_Error('aksrt_pay_locked', __('该订单正在发起支付,请稍后重试。', 'aksrt'), ['status' => 409]);
|
||||
}
|
||||
|
||||
$paid = \AKSRT\Payment\Order::get_instance()->update_status($order_number, \AKSRT\Payment\Order::STATUS_PAID, [
|
||||
'payment_method' => 'points',
|
||||
'payment_transaction_id' => 'points_' . $order_number,
|
||||
'note' => sprintf(__('积分兑换会员 %d 积分。', 'aksrt'), $cost),
|
||||
]);
|
||||
if (!$paid) {
|
||||
\AKSRT\Credits\Credits::add(
|
||||
try {
|
||||
$deducted = \AKSRT\Credits\Credits::deduct(
|
||||
$user_id,
|
||||
$cost,
|
||||
'vip_points_exchange_refund',
|
||||
['order' => $order_number],
|
||||
__('会员兑换失败退款', 'aksrt')
|
||||
'vip_points_exchange',
|
||||
['order' => $order_number, 'level' => $level, 'days' => $days],
|
||||
sprintf(__('积分兑换会员 · %s', 'aksrt'), (string) ($match['title'] ?? ''))
|
||||
);
|
||||
return new \WP_Error('aksrt_order_confirm_failed', __('兑换确认失败,已尝试退回积分', 'aksrt'), ['status' => 500]);
|
||||
if (is_wp_error($deducted) || $deducted === false) {
|
||||
\AKSRT\Payment\Order::get_instance()->update_status($order_number, \AKSRT\Payment\Order::STATUS_CANCELLED, [
|
||||
'note' => __('积分扣除失败,自动取消', 'aksrt'),
|
||||
]);
|
||||
$msg = is_wp_error($deducted) ? $deducted->get_error_message() : __('积分扣除失败', 'aksrt');
|
||||
return new \WP_Error('aksrt_credits_deduct_failed', $msg ?: __('积分扣除失败', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
|
||||
$paid = \AKSRT\Payment\Order::get_instance()->update_status($order_number, \AKSRT\Payment\Order::STATUS_PAID, [
|
||||
'payment_method' => 'points',
|
||||
'payment_transaction_id' => 'points_' . $order_number,
|
||||
'note' => sprintf(__('积分兑换会员 %d 积分。', 'aksrt'), $cost),
|
||||
'require_transition' => true,
|
||||
]);
|
||||
if (!$paid) {
|
||||
\AKSRT\Credits\Credits::add(
|
||||
$user_id,
|
||||
$cost,
|
||||
'vip_points_exchange_refund',
|
||||
['order' => $order_number],
|
||||
__('会员兑换失败退款', 'aksrt')
|
||||
);
|
||||
return new \WP_Error('aksrt_order_confirm_failed', __('兑换确认失败,已尝试退回积分', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
} finally {
|
||||
\AKSRT\Payment\PaymentAttemptLock::release($order_number, $lock);
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
Reference in new issue
Block a user