文件
xionggeshop/addons/shopro_huifu/library/HuifuSplitService.php

204 行
7.8 KiB
PHP

<?php
namespace addons\shopro_huifu\library;
use addons\shopro\library\split\SplitCalculator;
use addons\shopro\library\split\Config as SplitConfig;
use addons\shopro\model\Order;
use addons\shopro\model\split\SplitOrder;
use addons\shopro\model\split\SplitOrderReceiver;
use think\Db;
class HuifuSplitService
{
protected $client;
protected $config;
public function __construct()
{
$this->config = HuifuConfig::getInstance();
$this->client = new HuifuClient();
}
public function executeByOrderId($orderId)
{
$order = Order::get($orderId);
if (!$order || $order['pay_type'] !== 'huifu_wechat') {
return false;
}
$exists = SplitOrder::where('order_id', $orderId)->where('channel', 'huifu')->where('status', 'in', [1, 2])->find();
if ($exists) {
return true;
}
SplitOrder::where('order_id', $orderId)->where('channel', 'huifu')->where('status', 4)->delete();
$calculator = new SplitCalculator();
$items = $calculator->calculate($orderId);
if (empty($items)) {
$rewardCount = \addons\shopro\model\commission\Reward::where('order_id', $orderId)->where('status', '>=', 0)->count();
$regionalCount = \addons\shopro\model\regional_agent\Reward::where('order_id', $orderId)->where('status', '>=', 0)->count();
if ($rewardCount + $regionalCount > 0) {
\think\Log::warning('[shopro_huifu] split skipped order_id=' . $orderId . ' reason=receiver_missing_or_no_huifu_id');
}
return false;
}
$payFee = number_format((float) $order['pay_fee'], 2, '.', '');
$acctInfos = $this->buildAcctInfos($items, $payFee);
$splitAmount = '0.00';
foreach ($acctInfos as $info) {
$splitAmount = bcadd($splitAmount, $info['div_amt'], 2);
}
$outOrderNo = 'HFPS' . date('YmdHis') . $orderId;
$ext = HuifuPayService::getPayExt($orderId);
Db::startTrans();
try {
$splitOrder = SplitOrder::create([
'order_id' => $orderId,
'order_sn' => $order['order_sn'],
'channel' => 'huifu',
'transaction_id' => $order['transaction_id'],
'out_order_no' => $outOrderNo,
'total_amount' => $order['pay_fee'],
'split_amount' => $splitAmount,
'status' => 1,
]);
foreach ($items as $item) {
SplitOrderReceiver::create([
'split_order_id' => $splitOrder->id,
'receiver_id' => $item['receiver_id'],
'role_type' => $item['role_type'],
'amount' => $item['amount'],
'description' => $item['description'],
'status' => 0,
]);
}
$confirmResult = $this->confirm($ext, $acctInfos, $outOrderNo);
$splitOrder->status = 2;
$splitOrder->confirm_hf_seq_id = $confirmResult['hf_seq_id'] ?? ($confirmResult['confirm_hf_seq_id'] ?? '');
$splitOrder->hf_req_seq_id = $outOrderNo;
$splitOrder->wx_result = json_encode($confirmResult, JSON_UNESCAPED_UNICODE);
$splitOrder->save();
SplitOrderReceiver::where('split_order_id', $splitOrder->id)->update(['status' => 1]);
HuifuPayService::savePayExt($orderId, $order['order_sn'], [
'confirm_hf_seq_id' => $splitOrder->confirm_hf_seq_id,
'confirm_status' => 'S',
]);
Db::commit();
return true;
} catch (\Exception $e) {
Db::rollback();
$this->markFailed($orderId, $outOrderNo, $order, $items, $e->getMessage());
return false;
}
}
public function retry($splitOrderId)
{
$splitOrder = SplitOrder::with('receivers')->get($splitOrderId);
if (!$splitOrder || $splitOrder['channel'] !== 'huifu') {
return false;
}
return $this->executeByOrderId($splitOrder['order_id']);
}
protected function confirm($ext, array $acctInfos, $confirmReqSeqId)
{
SdkLoader::requireRequest('V2TradePaymentDelaytransConfirmRequest.php');
$request = new \BsPaySdk\request\V2TradePaymentDelaytransConfirmRequest();
$request->setReqDate(date('Ymd'));
$request->setReqSeqId($confirmReqSeqId);
$request->setHuifuId($this->config->get('huifu_id'));
$acctSplitBunch = json_encode(['acct_infos' => $acctInfos], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$request->setExtendInfo([
'org_req_date' => $ext['req_date'],
'org_req_seq_id' => $ext['req_seq_id'],
'org_hf_seq_id' => $ext['hf_seq_id'] ?? '',
'acct_split_bunch' => $acctSplitBunch,
]);
$result = $this->client->postRequest($request);
return $this->client->parseResponse($result);
}
protected function buildAcctInfos(array $items, $payFee)
{
$infos = [];
$allocated = '0.00';
foreach ($items as $item) {
$amt = number_format((float) $item['amount'], 2, '.', '');
if (bccomp($amt, '0.00', 2) <= 0) {
continue;
}
$infos[] = [
'huifu_id' => $item['receiver_account'],
'div_amt' => $amt,
];
$allocated = bcadd($allocated, $amt, 2);
}
$platformHuifuId = (new SplitConfig())->all()['platform_huifu_id'] ?? $this->config->get('huifu_id');
$remain = bcsub($payFee, $allocated, 2);
if (bccomp($remain, '0.00', 2) > 0) {
$infos[] = [
'huifu_id' => $platformHuifuId,
'div_amt' => $remain,
];
}
$sum = '0.00';
foreach ($infos as $info) {
$sum = bcadd($sum, $info['div_amt'], 2);
}
if (bccomp($sum, $payFee, 2) !== 0) {
throw new \RuntimeException('分账金额之和必须等于订单实付');
}
return $infos;
}
public function querySplit($hfSeqId)
{
SdkLoader::requireRequest('V2TradeTransSplitQueryRequest.php');
$request = new \BsPaySdk\request\V2TradeTransSplitQueryRequest();
$request->setHuifuId($this->config->get('huifu_id'));
$request->setHfSeqId($hfSeqId);
$request->setOrdType('consume');
$result = $this->client->postRequest($request);
return $this->client->parseResponse($result);
}
protected function markFailed($orderId, $outOrderNo, $order, $items, $message)
{
$splitOrder = SplitOrder::where('order_id', $orderId)->where('channel', 'huifu')->find();
$splitConfig = new SplitConfig();
if (!$splitOrder) {
SplitOrder::create([
'order_id' => $orderId,
'order_sn' => $order['order_sn'],
'channel' => 'huifu',
'transaction_id' => $order['transaction_id'] ?? '',
'out_order_no' => $outOrderNo,
'total_amount' => $order['pay_fee'],
'split_amount' => array_sum(array_column($items, 'amount')),
'status' => 4,
'wx_result' => $message,
'retry_count' => 1,
'next_retry_time'=> time() + $splitConfig->getRetryInterval(),
]);
} else {
$splitOrder->status = 4;
$splitOrder->wx_result = $message;
$splitOrder->retry_count = $splitOrder['retry_count'] + 1;
$splitOrder->next_retry_time = time() + $splitConfig->getRetryInterval();
$splitOrder->save();
}
}
}