fix: 优化退款防重与防多退机制,增加防重锁、事务排他行锁及汇付幂等对齐
这个提交包含在:
@@ -27,7 +27,57 @@ class HuifuRefundService
|
||||
if ($splitOrder && !empty($splitOrder['confirm_hf_seq_id'])) {
|
||||
return $this->confirmRefund($ext, $splitOrder, $refundLog, $refundMoney, $remark);
|
||||
}
|
||||
return $this->scanpayRefund($ext, $refundLog, $refundMoney, $remark);
|
||||
try {
|
||||
return $this->scanpayRefund($ext, $refundLog, $refundMoney, $remark);
|
||||
} catch (\Throwable $e) {
|
||||
// 如果汇付报错 23000003(申请退款金额大于可退款余额),核查是否此前已成功退款(幂等对齐防多退)
|
||||
if (strpos($e->getMessage(), '23000003') !== false || strpos($e->getMessage(), '申请退款金额大于可退款余额') !== false) {
|
||||
if ($this->isAlreadyRefunded($ext, $refundMoney)) {
|
||||
\think\Log::info('[shopro_huifu] 汇付检测到该笔退款此前已成功执行,执行本地状态幂等对齐: order_id=' . $order['id']);
|
||||
$refundLog->payment_json = json_encode(['idempotent' => true, 'remark' => '汇付核验已退款成功,自动对齐本地状态']);
|
||||
$refundLog->save();
|
||||
return true;
|
||||
}
|
||||
throw new \RuntimeException('汇付商户账户可用余额不足,请在汇付平台充值或留存可用资金后再退款');
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function isAlreadyRefunded($ext, $refundMoney)
|
||||
{
|
||||
try {
|
||||
SdkLoader::requireRequest('V2TradeAcctpaymentAcctlogQueryRequest.php');
|
||||
$request = new \BsPaySdk\request\V2TradeAcctpaymentAcctlogQueryRequest();
|
||||
$request->setReqSeqId(date('YmdHis') . mt_rand(1000, 9999));
|
||||
$request->setHuifuId($this->config->get('huifu_id'));
|
||||
$request->setAcctDate($ext['req_date'] ?? date('Ymd'));
|
||||
$request->setExtendInfo([
|
||||
'page_size' => '20',
|
||||
'page_num' => '1',
|
||||
'acct_type' => '01',
|
||||
]);
|
||||
|
||||
$result = $this->client->postRequest($request);
|
||||
$data = $this->client->parseResponse($result);
|
||||
if (empty($data['acct_log_list'])) {
|
||||
return false;
|
||||
}
|
||||
$list = is_string($data['acct_log_list']) ? json_decode($data['acct_log_list'], true) : $data['acct_log_list'];
|
||||
if (!is_array($list)) {
|
||||
return false;
|
||||
}
|
||||
$targetAmt = number_format((float)$refundMoney, 2, '.', '');
|
||||
foreach ($list as $log) {
|
||||
// 2006 为支付退款
|
||||
if (($log['acct_trans_type_code'] ?? '') === '2006' && ($log['trans_amt'] ?? '') === $targetAmt) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $ex) {
|
||||
\think\Log::error('[shopro_huifu] 幂等核查汇付退款流水异常: ' . $ex->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function scanpayRefund($ext, $refundLog, $refundMoney, $remark)
|
||||
|
||||
在新工单中引用
屏蔽一个用户