149 行
6.2 KiB
PHP
149 行
6.2 KiB
PHP
<?php
|
|
|
|
namespace addons\shopro_huifu\library;
|
|
|
|
use addons\shopro\model\split\SplitOrder;
|
|
use think\Db;
|
|
|
|
class HuifuRefundService
|
|
{
|
|
protected $client;
|
|
protected $config;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->config = HuifuConfig::getInstance();
|
|
$this->client = new HuifuClient();
|
|
}
|
|
|
|
public function refund($order, $refundLog, $refundMoney, $remark = '')
|
|
{
|
|
$ext = HuifuPayService::getPayExt($order['id']);
|
|
if (!$ext) {
|
|
throw new \RuntimeException('未找到汇付支付记录');
|
|
}
|
|
|
|
$splitOrder = SplitOrder::where('order_id', $order['id'])->where('channel', 'huifu')->where('status', 2)->find();
|
|
try {
|
|
if ($splitOrder && !empty($splitOrder['confirm_hf_seq_id'])) {
|
|
return $this->confirmRefund($ext, $splitOrder, $refundLog, $refundMoney, $remark);
|
|
}
|
|
return $this->scanpayRefund($ext, $refundLog, $refundMoney, $remark);
|
|
} catch (\Throwable $e) {
|
|
// 核查是否此前已在汇付成功退款(网关状态幂等自动对齐,防多退、防报错卡死)
|
|
if ($this->isAlreadyRefunded($ext, $refundMoney)) {
|
|
\think\Log::info('[shopro_huifu] 汇付检测到该笔退款此前已成功执行,执行本地状态自动对齐: order_id=' . $order['id']);
|
|
$refundLog->payment_json = json_encode([
|
|
'idempotent' => true,
|
|
'remark' => '汇付核验已退款成功,自动对齐本地状态',
|
|
'raw_error' => $e->getMessage(),
|
|
], JSON_UNESCAPED_UNICODE);
|
|
$refundLog->save();
|
|
return true;
|
|
}
|
|
|
|
if (strpos($e->getMessage(), '23000003') !== false || strpos($e->getMessage(), '申请退款金额大于可退款余额') !== false) {
|
|
throw new \RuntimeException('汇付商户账户可用余额不足,请在汇付平台充值或留存可用资金后再退款');
|
|
}
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
public function isAlreadyRefunded($ext, $refundMoney)
|
|
{
|
|
$dates = array_filter(array_unique([
|
|
date('Ymd'),
|
|
!empty($ext['req_date']) ? $ext['req_date'] : null,
|
|
]));
|
|
|
|
foreach ($dates as $date) {
|
|
if ($this->checkAcctLogForRefund($date, $refundMoney)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected function checkAcctLogForRefund($acctDate, $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($acctDate);
|
|
$request->setExtendInfo([
|
|
'page_size' => '50',
|
|
'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] 幂等核查汇付退款流水异常(' . $acctDate . '): ' . $ex->getMessage());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected function scanpayRefund($ext, $refundLog, $refundMoney, $remark)
|
|
{
|
|
SdkLoader::requireRequest('v4/payment/TradePaymentScanpayRefundRequest.php');
|
|
$request = new \BsPaySdk\request\v4\payment\TradePaymentScanpayRefundRequest();
|
|
$request->setReqDate(date('Ymd'));
|
|
$request->setReqSeqId($refundLog['refund_sn']);
|
|
$request->setHuifuId($this->config->get('huifu_id'));
|
|
$request->setOrdAmt(number_format((float) $refundMoney, 2, '.', ''));
|
|
$request->setOrgReqDate($ext['req_date']);
|
|
$request->setOrgReqSeqId($ext['req_seq_id']);
|
|
if ($this->config->get('refund_notify_url')) {
|
|
$request->setNotifyUrl($this->config->getNotifyUrl('refund'));
|
|
}
|
|
if ($remark) {
|
|
$request->setRemark($remark);
|
|
}
|
|
$result = $this->client->payment()->refund($request);
|
|
$data = $this->client->parseResponse($result);
|
|
if (($data['trans_stat'] ?? '') === 'S' || ($data['resp_code'] ?? '') === '00000000') {
|
|
return true;
|
|
}
|
|
throw new \RuntimeException($data['resp_desc'] ?? '汇付退款失败');
|
|
}
|
|
|
|
protected function confirmRefund($ext, $splitOrder, $refundLog, $refundMoney, $remark)
|
|
{
|
|
SdkLoader::requireRequest('V2TradePaymentDelaytransConfirmrefundRequest.php');
|
|
$request = new \BsPaySdk\request\V2TradePaymentDelaytransConfirmrefundRequest();
|
|
$request->setReqDate(date('Ymd'));
|
|
$request->setReqSeqId($refundLog['refund_sn']);
|
|
$request->setHuifuId($this->config->get('huifu_id'));
|
|
$request->setExtendInfo([
|
|
'org_req_date' => $ext['req_date'],
|
|
'org_req_seq_id' => $ext['req_seq_id'],
|
|
'confirm_hf_seq_id' => $splitOrder['confirm_hf_seq_id'],
|
|
'ord_amt' => number_format((float) $refundMoney, 2, '.', ''),
|
|
'remark' => $remark,
|
|
]);
|
|
$result = $this->client->postRequest($request);
|
|
$data = $this->client->parseResponse($result);
|
|
if (($data['trans_stat'] ?? '') === 'S' || ($data['resp_code'] ?? '') === '00000000') {
|
|
return true;
|
|
}
|
|
throw new \RuntimeException($data['resp_desc'] ?? '汇付确认退款失败');
|
|
}
|
|
}
|