全部
常见问题
产品动态
精选推荐
功能建议

分析中 已回复 待规划 {{opt.name}}
分析中 已回复 待规划
CRMEB多门店PHP源码系统对接汇付支付商家分账完整流程代码示例

管理 管理 编辑 删除

目录结构


project/
├── app/
│   ├── common.php
│   ├── config/
│   │   └── payment.php
│   ├── controller/
│   │   ├── PaymentController.php
│   │   ├── StoreController.php
│   │   └── AdminController.php
│   ├── model/
│   │   ├── Order.php
│   │   ├── Store.php
│   │   ├── PaymentLog.php
│   │   └── SplitAccount.php
│   ├── service/
│   │   ├── HuifuService.php
│   │   ├── SplitService.php
│   │   └── StoreAccountService.php
│   └── validate/
│       └── PaymentValidate.php
├── database/
│   └── migrations/
│       └── create_split_tables.php
└── vendor/

1. 数据库表结构


<?php
// database/migrations/create_split_tables.php

use think\migration\Migrator;
use think\migration\db\Column;

class CreateSplitTables extends Migrator
{
    public function change()
    {
        // 门店分账账户表
        $this->table('store_split_account', ['comment' => '门店分账账户'])
            ->addColumn('store_id', 'integer', ['comment' => '门店ID'])
            ->addColumn('huifu_merchant_id', 'string', ['limit' => 50, 'comment' => '汇付商户号'])
            ->addColumn('split_ratio', 'decimal', ['precision' => 5, 'scale' => 2, 'comment' => '分账比例(%)'])
            ->addColumn('settlement_cycle', 'string', ['limit' => 20, 'comment' => '结算周期:T0/T1/D1'])
            ->addColumn('bank_account_name', 'string', ['limit' => 100, 'comment' => '银行开户名'])
            ->addColumn('bank_account_no', 'string', ['limit' => 50, 'comment' => '银行账号'])
            ->addColumn('bank_name', 'string', ['limit' => 100, 'comment' => '开户银行'])
            ->addColumn('bank_code', 'string', ['limit' => 20, 'comment' => '银行代码'])
            ->addColumn('status', 'boolean', ['default' => 1, 'comment' => '状态:0禁用 1启用'])
            ->addColumn('create_time', 'integer', ['comment' => '创建时间'])
            ->addColumn('update_time', 'integer', ['comment' => '更新时间'])
            ->addIndex(['store_id'], ['unique' => true])
            ->create();

        // 订单分账记录表
        $this->table('order_split_record', ['comment' => '订单分账记录'])
            ->addColumn('order_id', 'integer', ['comment' => '订单ID'])
            ->addColumn('order_no', 'string', ['limit' => 50, 'comment' => '订单号'])
            ->addColumn('store_id', 'integer', ['comment' => '门店ID'])
            ->addColumn('platform_order_no', 'string', ['limit' => 100, 'comment' => '汇付订单号'])
            ->addColumn('total_amount', 'decimal', ['precision' => 10, 'scale' => 2, 'comment' => '订单总金额'])
            ->addColumn('split_amount', 'decimal', ['precision' => 10, 'scale' => 2, 'comment' => '分账金额'])
            ->addColumn('platform_fee', 'decimal', ['precision' => 10, 'scale' => 2, 'comment' => '平台手续费'])
            ->addColumn('split_status', 'boolean', ['default' => 0, 'comment' => '分账状态:0待分账 1分账中 2分账成功 3分账失败'])
            ->addColumn('split_result', 'text', ['null' => true, 'comment' => '分账结果'])
            ->addColumn('error_msg', 'string', ['limit' => 500, 'null' => true, 'comment' => '错误信息'])
            ->addColumn('create_time', 'integer', ['comment' => '创建时间'])
            ->addColumn('update_time', 'integer', ['comment' => '更新时间'])
            ->addIndex(['order_no'])
            ->addIndex(['store_id'])
            ->create();

        // 门店手续费记录表
        $this->table('store_fee_record', ['comment' => '门店手续费记录'])
            ->addColumn('store_id', 'integer', ['comment' => '门店ID'])
            ->addColumn('order_id', 'integer', ['comment' => '订单ID'])
            ->addColumn('order_no', 'string', ['limit' => 50, 'comment' => '订单号'])
            ->addColumn('fee_amount', 'decimal', ['precision' => 10, 'scale' => 2, 'comment' => '手续费金额'])
            ->addColumn('fee_rate', 'decimal', ['precision' => 5, 'scale' => 2, 'comment' => '手续费率(%)'])
            ->addColumn('create_time', 'integer', ['comment' => '创建时间'])
            ->addIndex(['store_id'])
            ->create();

        // 汇付支付日志表
        $this->table('huifu_payment_log', ['comment' => '汇付支付日志'])
            ->addColumn('type', 'string', ['limit' => 50, 'comment' => '日志类型:payment/split/refund'])
            ->addColumn('order_no', 'string', ['limit' => 50, 'comment' => '订单号'])
            ->addColumn('request_data', 'text', ['comment' => '请求数据'])
            ->addColumn('response_data', 'text', ['comment' => '响应数据'])
            ->addColumn('status', 'boolean', ['default' => 0, 'comment' => '状态:0失败 1成功'])
            ->addColumn('error_code', 'string', ['limit' => 50, 'null' => true, 'comment' => '错误代码'])
            ->addColumn('error_msg', 'string', ['limit' => 500, 'null' => true, 'comment' => '错误信息'])
            ->addColumn('create_time', 'integer', ['comment' => '创建时间'])
            ->addIndex(['type', 'order_no'])
            ->create();
    }
}

2. 配置文件


<?php
// app/config/payment.php

return [
    // 汇付支付配置
    'huifu' => [
        'app_id' => env('HUIFU_APP_ID', ''),
        'secret_key' => env('HUIFU_SECRET_KEY', ''),
        'merchant_id' => env('HUIFU_MERCHANT_ID', ''),
        'api_url' => env('HUIFU_API_URL', 'https://api.huifu.com'),
        'notify_url' => env('HUIFU_NOTIFY_URL', 'https://yourdomain.com/api/payment/notify'),
        'return_url' => env('HUIFU_RETURN_URL', 'https://yourdomain.com/payment/return'),
        'split_notify_url' => env('HUIFU_SPLIT_NOTIFY_URL', 'https://yourdomain.com/api/payment/splitNotify'),
        'timeout' => 30,
    ],
    
    // 平台手续费配置
    'platform_fee' => [
        'default_rate' => 0.6, // 默认手续费率(%)
        'min_fee' => 0.01, // 最低手续费
        'max_fee' => 1000, // 最高手续费
    ],
    
    // 分账配置
 'split' => [
        'auto_split' => true, // 是否自动分账
        'split_time' => 30, // 分账延迟时间(分钟),用于确认收货后退款风险
        'retry_times' => 3, // 失败重试次数
        'retry_interval' => 5, // 重试间隔(分钟)
    ],
];

3. 核心服务类 - 汇付支付对接


<?php
// app/service/HuifuService.php

namespace app\service;

use think\facade\Log;
use think\facade\Cache;
use app\model\HuifuPaymentLog;

class HuifuService
{
    private $appId;
    private $secretKey;
    private $merchantId;
    private $apiUrl;
    private $notifyUrl;
    private $splitNotifyUrl;
    private $timeout;

    public function __construct()
    {
        $config = config('payment.huifu');
        $this->appId = $config['app_id'];
        $this->secretKey = $config['secret_key'];
        $this->merchantId = $config['merchant_id'];
        $this->apiUrl = $config['api_url'];
        $this->notifyUrl = $config['notify_url'];
        $this->splitNotifyUrl = $config['split_notify_url'];
        $this->timeout = $config['timeout'];
    }

    /**
     * 统一下单支付接口
     */
    public function unifiedOrder($orderNo, $amount, $subject, $body, $storeId = null)
    {
        $params = [
            'app_id' => $this->appId,
            'merchant_id' => $this->merchantId,
            'out_trade_no' => $orderNo,
            'total_amount' => $this->formatAmount($amount),
            'subject' => $subject,
            'body' => $body,
            'notify_url' => $this->notifyUrl,
            'return_url' => config('payment.huifu.return_url'),
            'timeout_express' => '30m',
            'pay_type' => 'WEIXIN_JSAPI', // 支付方式,可根据前端传入调整
        ];

        // 如果有门店,添加分账标识
        if ($storeId) {
            $params['split_flag'] = 'Y';
            $params['split_notify_url'] = $this->splitNotifyUrl;
        }

        // 生成签名
        $params['sign'] = $this->generateSign($params);

        // 记录请求日志
        $this->logRequest('unified_order', $orderNo, $params);

        // 发送请求
        $response = $this->post('/v2/payment/unifiedOrder', $params);

        // 记录响应日志
        $this->logResponse('unified_order', $orderNo, $response);

        return $response;
    }

    /**
     * 订单分账接口
     */
    public function orderSplit($orderNo, $totalAmount, $splitList)
    {
        $params = [
            'app_id' => $this->appId,
            'merchant_id' => $this->merchantId,
            'out_trade_no' => $orderNo,
            'total_amount' => $this->formatAmount($totalAmount),
            'split_list' => $splitList,
        ];

        $params['sign'] = $this->generateSign($params);
        
        $this->logRequest('order_split', $orderNo, $params);
        
        $response = $this->post('/v2/payment/split', $params);
        
        $this->logResponse('order_split', $orderNo, $response);
        
        return $response;
    }

    /**
     * 分账查询接口
     */
    public function querySplit($orderNo)
    {
        $params = [
            'app_id' => $this->appId,
            'merchant_id' => $this->merchantId,
            'out_trade_no' => $orderNo,
        ];

        $params['sign'] = $this->generateSign($params);
        
        $response = $this->post('/v2/payment/querySplit', $params);
        
        return $response;
    }

    /**
     * 退款接口
     */
    public function refund($orderNo, $refundNo, $refundAmount, $totalAmount)
    {
        $params = [
            'app_id' => $this->appId,
            'merchant_id' => $this->merchantId,
            'out_trade_no' => $orderNo,
            'out_refund_no' => $refundNo,
            'refund_amount' => $this->formatAmount($refundAmount),
            'total_amount' => $this->formatAmount($totalAmount),
        ];

        $params['sign'] = $this->generateSign($params);
        
        $this->logRequest('refund', $orderNo, $params);
        
        $response = $this->post('/v2/payment/refund', $params);
        
        $this->logResponse('refund', $orderNo, $response);
        
        return $response;
    }

    /**
     * 订单查询接口
     */
    public function queryOrder($orderNo)
    {
        $params = [
            'app_id' => $this->appId,
            'merchant_id' => $this->merchantId,
            'out_trade_no' => $orderNo,
        ];

        $params['sign'] = $this->generateSign($params);
        
        $response = $this->post('/v2/payment/query', $params);
        
        return $response;
    }

    /**
     * 生成门店分账列表
     */
    public function buildSplitList($storeSplits)
    {
        $splitList = [];
        foreach ($storeSplits as $split) {
            $splitList[] = [
                'merchant_id' => $split['huifu_merchant_id'],
                'amount' => $this->formatAmount($split['amount']),
                'ratio' => $split['ratio'] ?? null,
                'desc' => $split['desc'] ?? '商品销售分账',
            ];
        }
        return $splitList;
    }

    /**
     * 验证回调签名
     */
    public function verifyNotifySign($params)
    {
        if (!isset($params['sign'])) {
            return false;
        }
        
        $sign = $params['sign'];
        unset($params['sign']);
        
        $calculatedSign = $this->generateSign($params);
        
        return $sign === $calculatedSign;
    }

    /**
     * 生成签名
     */
    private function generateSign($params)
    {
        // 按照ASCII码排序
        ksort($params);
        
        $str = '';
        foreach ($params as $key => $value) {
            if ($value !== '' && $value !== null && $key !== 'sign') {
                $str .= $key . '=' . $value . '&';
            }
        }
        
        $str = rtrim($str, '&');
        $str .= '&key=' . $this->secretKey;
        
        return strtoupper(md5($str));
    }

    /**
     * 发送POST请求
     */
    private function post($path, $params)
    {
        $url = $this->apiUrl . $path;
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Accept: application/json',
        ]);
        
        $response = curl_exec($ch);
        $error = curl_error($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($error) {
            Log::error('汇付支付请求失败: ' . $error);
            return ['code' => 'ERROR', 'msg' => '网络请求失败: ' . $error];
        }
        
        if ($httpCode != 200) {
            Log::error('汇付支付HTTP错误: ' . $httpCode);
            return ['code' => 'ERROR', 'msg' => 'HTTP错误: ' . $httpCode];
        }
        
        $result = json_decode($response, true);
        
        if (!$result) {
            return ['code' => 'ERROR', 'msg' => '响应解析失败'];
        }
        
        return $result;
    }

    /**
     * 格式化金额(分转元,保留2位小数)
     */
    private function formatAmount($amount)
    {
        return number_format($amount / 100, 2, '.', '');
    }

    /**
     * 记录请求日志
     */
    private function logRequest($type, $orderNo, $params)
    {
        try {
            HuifuPaymentLog::create([
                'type' => $type,
                'order_no' => $orderNo,
                'request_data' => json_encode($params, JSON_UNESCAPED_UNICODE),
                'create_time' => time(),
            ]);
        } catch (\Exception $e) {
            Log::error('记录请求日志失败: ' . $e->getMessage());
        }
    }

    /**
     * 记录响应日志
     */
    private function logResponse($type, $orderNo, $response)
    {
        try {
            $log = HuifuPaymentLog::where('type', $type)
                ->where('order_no', $orderNo)
                ->order('id', 'desc')
                ->find();
                
            if ($log) {
                $log->response_data = json_encode($response, JSON_UNESCAPED_UNICODE);
                $log->status = isset($response['code']) && $response['code'] == 'SUCCESS' ? 1 : 0;
                if (!$log->status) {
                    $log->error_code = $response['code'] ?? '';
                    $log->error_msg = $response['msg'] ?? '';
                }
                $log->save();
            }
        } catch (\Exception $e) {
            Log::error('记录响应日志失败: ' . $e->getMessage());
        }
    }
}

4. 分账服务类


<?php
// app/service/SplitService.php

namespace app\service;

use think\facade\Log;
use think\facade\Db;
use app\model\Order;
use app\model\Store;
use app\model\OrderSplitRecord;
use app\model\StoreSplitAccount;
use app\model\StoreFeeRecord;
use app\service\HuifuService;

class SplitService
{
    private $huifuService;
    
    public function __construct()
    {
        $this->huifuService = new HuifuService();
    }
    
    /**
     * 处理订单分账
     */
    public function processOrderSplit($orderId)
    {
        Db::startTrans();
        try {
            $order = Order::with(['orderGoods', 'store'])->find($orderId);
            
            if (!$order || $order['pay_status'] != 1) {
                throw new \Exception('订单不存在或未支付');
            }
            
            // 检查是否已经分账
            $exists = OrderSplitRecord::where('order_id', $orderId)
                ->whereIn('split_status', [1, 2])
                ->find();
                
            if ($exists) {
                throw new \Exception('订单已处理分账');
            }
            
            // 获取门店分账配置
            $storeSplits = $this->getStoreSplits($order['order_goods']);
            
            if (empty($storeSplits)) {
                // 没有门店需要分账,直接标记为已完成
                $this->markOrderSplitComplete($orderId);
                Db::commit();
                return true;
            }
            
            // 计算各门店分账金额
            $splitList = $this->calculateStoreSplit($order, $storeSplits);
            
            // 计算平台手续费
            $platformFee = $this->calculatePlatformFee($order, $storeSplits);
            
            // 创建分账记录
            $splitRecord = $this->createSplitRecord($order, $splitList, $platformFee);
            
            // 如果不需要自动分账,则暂不处理
            if (!config('payment.split.auto_split')) {
                Db::commit();
                return true;
            }
            
            // 判断是否需要延迟分账
            $needDelay = $this->needDelaySplit($order);
            
            if ($needDelay) {
                // 加入延迟队列
                $this->addToSplitQueue($orderId, config('payment.split.split_time'));
                Db::commit();
                return true;
            }
            
            // 立即执行分账
            $result = $this->executeSplit($order, $splitList, $splitRecord);
            
            Db::commit();
            
            return $result;
            
        } catch (\Exception $e) {
            Db::rollback();
            Log::error('处理订单分账失败: ' . $e->getMessage() . ',订单ID:' . $orderId);
            return false;
        }
    }
    
    /**
     * 执行分账
     */
    public function executeSplit($order, $splitList, $splitRecord = null)
    {
        try {
            // 构建分账列表
            $huifuSplitList = $this->huifuService->buildSplitList($splitList);
            
            // 调用汇付分账接口
            $response = $this->huifuService->orderSplit(
                $order['order_no'],
                $order['pay_amount'],
                $huifuSplitList
            );
            
            if (!$splitRecord) {
                $splitRecord = OrderSplitRecord::where('order_id', $order['id'])->find();
            }
            
            if ($response['code'] == 'SUCCESS') {
                // 分账成功
                $splitRecord->split_status = 2;
                $splitRecord->split_result = json_encode($response);
                $splitRecord->update_time = time();
                $splitRecord->save();
                
                // 记录分账成功日志
                Log::info('订单分账成功:' . $order['order_no']);
                
                return true;
            } else {
                // 分账失败
                $splitRecord->split_status = 3;
                $splitRecord->error_msg = $response['msg'] ?? '分账失败';
                $splitRecord->update_time = time();
                $splitRecord->save();
                
                // 加入重试队列
                $this->addToRetryQueue($order['id']);
                
                Log::error('订单分账失败:' . $order['order_no'] . ',错误:' . ($response['msg'] ?? ''));
                
                return false;
            }
            
        } catch (\Exception $e) {
            Log::error('执行分账异常:' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * 获取门店分账配置
     */
    private function getStoreSplits($orderGoods)
    {
        $storeIds = array_unique(array_column($orderGoods, 'store_id'));
        
        if (empty($storeIds)) {
            return [];
        }
        
        $stores = Store::whereIn('id', $storeIds)
            ->where('status', 1)
            ->column('*', 'id');
            
        $accounts = StoreSplitAccount::whereIn('store_id', $storeIds)
            ->where('status', 1)
            ->column('*', 'store_id');
            
        $result = [];
        foreach ($orderGoods as $goods) {
            $storeId = $goods['store_id'];
            if (!isset($result[$storeId])) {
                $result[$storeId] = [
                    'store_id' => $storeId,
                    'store_name' => $stores[$storeId]['name'] ?? '',
                    'huifu_merchant_id' => $accounts[$storeId]['huifu_merchant_id'] ?? '',
                    'split_ratio' => $accounts[$storeId]['split_ratio'] ?? 100,
                    'goods_amount' => 0,
                ];
            }
            $result[$storeId]['goods_amount'] += $goods['pay_price'] * $goods['goods_num'];
        }
        
        return $result;
    }
    
    /**
     * 计算门店分账金额
     */
    private function calculateStoreSplit($order, $storeSplits)
    {
        $totalGoodsAmount = array_sum(array_column($storeSplits, 'goods_amount'));
        $splitList = [];
        
        foreach ($storeSplits as $storeId => $store) {
            // 按商品金额比例分配订单实际支付金额
            $ratio = $store['goods_amount'] / $totalGoodsAmount;
            $storeAmount = bcmul($order['pay_amount'], $ratio, 2);
            
            // 应用门店分账比例
            $splitAmount = bcmul($storeAmount, bcdiv($store['split_ratio'], 100, 4), 2);
            
            $splitList[] = [
                'merchant_id' => $store['huifu_merchant_id'],
                'store_id' => $storeId,
                'amount' => bcmul($splitAmount, 100, 0), // 转为分
                'ratio' => $store['split_ratio'],
                'desc' => $store['store_name'] . '商品销售分账',
            ];
            
            // 记录手续费
            $feeAmount = bcsub($storeAmount, $splitAmount, 2);
            if ($feeAmount > 0) {
                $this->recordStoreFee($storeId, $order['id'], $order['order_no'], $feeAmount, $store['split_ratio']);
            }
        }
        
        return $splitList;
    }
    
    /**
     * 计算平台手续费
     */
    private function calculatePlatformFee($order, $storeSplits)
    {
        $totalPlatformFee = 0;
        $defaultRate = config('payment.platform_fee.default_rate');
        
        foreach ($storeSplits as $store) {
            // 获取门店手续费率,可配置不同门店不同费率
            $feeRate = $store['split_ratio'] ?? $defaultRate;
            $fee = bcmul($store['goods_amount'], bcdiv($feeRate, 100, 4), 2);
            
            // 手续费限制
            $minFee = config('payment.platform_fee.min_fee');
            $maxFee = config('payment.platform_fee.max_fee');
            
            if ($fee < $minFee) {
                $fee = $minFee;
            }
            if ($fee > $maxFee) {
                $fee = $maxFee;
            }
            
            $totalPlatformFee = bcadd($totalPlatformFee, $fee, 2);
        }
        
        return $totalPlatformFee;
    }
    
    /**
     * 创建分账记录
     */
    private function createSplitRecord($order, $splitList, $platformFee)
    {
        $record = OrderSplitRecord::create([
            'order_id' => $order['id'],
            'order_no' => $order['order_no'],
            'store_id' => $splitList[0]['store_id'] ?? 0,
            'total_amount' => $order['pay_amount'],
            'split_amount' => array_sum(array_column($splitList, 'amount')) / 100,
            'platform_fee' => $platformFee,
            'split_status' => 0,
            'create_time' => time(),
        ]);
        
        return $record;
    }
    
    /**
     * 标记订单分账完成(无门店分账情况)
     */
    private function markOrderSplitComplete($orderId)
    {
        OrderSplitRecord::create([
            'order_id' => $orderId,
            'split_status' => 2,
            'create_time' => time(),
            'update_time' => time(),
        ]);
    }
    
    /**
     * 判断是否需要延迟分账
     */
    private function needDelaySplit($order)
    {
        // 判断是否是虚拟商品、是否已完成等
        if ($order['order_type'] == 'virtual') {
            return false; // 虚拟商品即时分账
        }
        
        return true; // 实物商品延迟分账
    }
    
    /**
     * 记录门店手续费
     */
    private function recordStoreFee($storeId, $orderId, $orderNo, $feeAmount, $feeRate)
    {
        StoreFeeRecord::create([
            'store_id' => $storeId,
            'order_id' => $orderId,
            'order_no' => $orderNo,
            'fee_amount' => $feeAmount,
            'fee_rate' => $feeRate,
            'create_time' => time(),
        ]);
    }
    
    /**
     * 加入分账队列
     */
    private function addToSplitQueue($orderId, $delayMinutes)
    {
        $key = 'split_queue_' . $orderId;
        $data = [
            'order_id' => $orderId,
            'execute_time' => time() + $delayMinutes * 60,
        ];
        Cache::set($key, $data, $delayMinutes * 60 + 60);
    }
    
    /**
     * 加入重试队列
     */
    private function addToRetryQueue($orderId)
    {
        $key = 'split_retry_' . $orderId;
        $retryCount = Cache::get($key . '_count', 0);
        
        if ($retryCount >= config('payment.split.retry_times')) {
            Log::error('订单分账重试次数已达上限:' . $orderId);
            return false;
        }
        
        $retryCount++;
        $delayMinutes = config('payment.split.retry_interval');
        
        $data = [
            'order_id' => $orderId,
            'retry_count' => $retryCount,
            'execute_time' => time() + $delayMinutes * 60,
        ];
        
        Cache::set($key, $data, $delayMinutes * 60 + 60);
        Cache::set($key . '_count', $retryCount, 86400);
        
        return true;
    }
    
    /**
     * 处理退款分账
     */
    public function processRefundSplit($orderId, $refundAmount)
    {
        Db::startTrans();
        try {
            $splitRecord = OrderSplitRecord::where('order_id', $orderId)
                ->where('split_status', 2)
                ->find();
                
            if (!$splitRecord) {
                throw new \Exception('未找到分账记录');
            }
            
            // 更新分账记录,标记为退款中
            $splitRecord->split_status = 4; // 自定义状态:退款中
            $splitRecord->save();
            
            // 记录退款日志
            Log::info('订单退款分账处理:' . $orderId . ',退款金额:' . $refundAmount);
            
            Db::commit();
            return true;
            
        } catch (\Exception $e) {
            Db::rollback();
            Log::error('处理退款分账失败:' . $e->getMessage());
            return false;
        }
    }
}

5. 门店账户服务类


<?php
// app/service/StoreAccountService.php

namespace app\service;

use think\facade\Db;
use app\model\Store;
use app\model\StoreSplitAccount;

class StoreAccountService
{
    /**
     * 创建门店分账账户
     */
    public function createStoreAccount($storeId, $data)
    {
        return Db::transaction(function () use ($storeId, $data) {
            // 检查是否已存在
            $exists = StoreSplitAccount::where('store_id', $storeId)->find();
            
            if ($exists) {
                throw new \Exception('门店分账账户已存在');
            }
            
            // 验证银行信息
            $this->validateBankInfo($data);
            
            // 创建账户
            $account = StoreSplitAccount::create([
                'store_id' => $storeId,
                'huifu_merchant_id' => $data['huifu_merchant_id'],
                'split_ratio' => $data['split_ratio'] ?? 100,
                'settlement_cycle' => $data['settlement_cycle'] ?? 'T1',
                'bank_account_name' => $data['bank_account_name'],
                'bank_account_no' => $data['bank_account_no'],
                'bank_name' => $data['bank_name'],
                'bank_code' => $data['bank_code'],
                'status' => $data['status'] ?? 1,
                'create_time' => time(),
                'update_time' => time(),
            ]);
            
            return $account;
        });
    }
    
    /**
     * 更新门店分账账户
     */
    public function updateStoreAccount($storeId, $data)
    {
        $account = StoreSplitAccount::where('store_id', $storeId)->find();
        
        if (!$account) {
            throw new \Exception('门店分账账户不存在');
        }
        
        // 验证银行信息
        if (isset($data['bank_account_no'])) {
            $this->validateBankInfo($data);
        }
        
        $data['update_time'] = time();
        $account->save($data);
        
        return $account;
    }
    
    /**
     * 批量导入门店分账账户
     */
    public function batchImportAccounts($accounts)
    {
        $success = 0;
        $fail = 0;
        $errors = [];
        
        Db::startTrans();
        try {
            foreach ($accounts as $account) {
                try {
                    // 检查门店是否存在
                    $store = Store::find($account['store_id']);
                    if (!$store) {
                        throw new \Exception('门店不存在:' . $account['store_id']);
                    }
                    
                    // 创建或更新
                    $exists = StoreSplitAccount::where('store_id', $account['store_id'])->find();
                    
                    $data = [
                        'huifu_merchant_id' => $account['huifu_merchant_id'],
                        'split_ratio' => $account['split_ratio'] ?? 100,
                        'settlement_cycle' => $account['settlement_cycle'] ?? 'T1',
                        'bank_account_name' => $account['bank_account_name'],
                        'bank_account_no' => $account['bank_account_no'],
                        'bank_name' => $account['bank_name'],
                        'bank_code' => $account['bank_code'],
                        'status' => $account['status'] ?? 1,
                        'update_time' => time(),
                    ];
                    
                    if ($exists) {
                        $exists->save($data);
                    } else {
                        $data['store_id'] = $account['store_id'];
                        $data['create_time'] = time();
                        StoreSplitAccount::create($data);
                    }
                    
                    $success++;
                    
                } catch (\Exception $e) {
                    $fail++;
                    $errors[] = '门店ID ' . $account['store_id'] . ' 失败:' . $e->getMessage();
                }
            }
            
            Db::commit();
            
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
        
        return [
            'success' => $success,
            'fail' => $fail,
            'errors' => $errors,
        ];
    }
    
    /**
     * 验证银行信息
     */
    private function validateBankInfo($data)
    {
        if (empty($data['bank_account_name'])) {
            throw new \Exception('银行开户名不能为空');
        }
        
        if (empty($data['bank_account_no'])) {
            throw new \Exception('银行账号不能为空');
        }
        
        if (empty($data['bank_name'])) {
            throw new \Exception('开户银行不能为空');
        }
        
        // 验证银行卡号格式(简单验证)
        if (!preg_match('/^\d{16,19}$/', $data['bank_account_no'])) {
            throw new \Exception('银行卡号格式不正确');
        }
    }
    
    /**
     * 获取门店分账统计
     */
    public function getStoreSplitStats($storeId, $startDate, $endDate)
    {
        $stats = Db::name('order_split_record')
            ->where('store_id', $storeId)
            ->whereBetweenTime('create_time', strtotime($startDate), strtotime($endDate))
            ->field([
                'COUNT(*) as total_count',
                'SUM(split_amount) as total_split_amount',
                'SUM(platform_fee) as total_fee',
                'SUM(CASE WHEN split_status = 2 THEN split_amount ELSE 0 END) as success_amount',
            ])
            ->find();
            
        return $stats;
    }
}

6. 支付控制器


<?php
// app/controller/PaymentController.php

namespace app\controller;

use think\Request;
use think\facade\Log;
use app\BaseController;
use app\model\Order;
use app\model\PaymentLog;
use app\service\HuifuService;
use app\service\SplitService;

class PaymentController extends BaseController
{
    private $huifuService;
    private $splitService;
    
    public function __construct()
    {
        $this->huifuService = new HuifuService();
        $this->splitService = new SplitService();
    }
    
    /**
     * 统一下单
     */
    public function unifiedOrder(Request $request)
    {
        $orderNo = $request->param('order_no');
        $payType = $request->param('pay_type', 'WEIXIN_JSAPI');
        
        $order = Order::with('store')->where('order_no', $orderNo)->find();
        
        if (!$order) {
            return json(['code' => 1, 'msg' => '订单不存在']);
        }
        
        if ($order['pay_status'] == 1) {
            return json(['code' => 1, 'msg' => '订单已支付']);
        }
        
        // 调用汇付统一下单
        $response = $this->huifuService->unifiedOrder(
            $order['order_no'],
            $order['pay_amount'],
            $order['order_name'],
            $order['order_desc'],
            $order['store_id']
        );
        
        if ($response['code'] != 'SUCCESS') {
            return json(['code' => 1, 'msg' => '下单失败:' . ($response['msg'] ?? '')]);
        }
        
        // 返回支付参数
        return json([
            'code' => 0,
            'msg' => 'success',
            'data' => [
                'pay_params' => $response['pay_params'],
                'order_no' => $order['order_no'],
            ],
        ]);
    }
    
    /**
     * 支付回调通知
     */
    public function notify(Request $request)
    {
        $params = $request->post();
        
        Log::info('汇付支付回调:' . json_encode($params));
        
        // 验证签名
        if (!$this->huifuService->verifyNotifySign($params)) {
            Log::error('汇付支付回调签名验证失败');
            return 'fail';
        }
        
        $orderNo = $params['out_trade_no'];
        $tradeNo = $params['trade_no'];
        $payStatus = $params['trade_status'];
        
        if ($payStatus == 'SUCCESS') {
            // 处理订单支付成功
            $result = $this->processPaidOrder($orderNo, $tradeNo, $params);
            
            if ($result) {
                return 'success';
            }
        }
        
        return 'fail';
    }
    
    /**
     * 分账回调通知
     */
    public function splitNotify(Request $request)
    {
        $params = $request->post();
        
        Log::info('汇付分账回调:' . json_encode($params));
        
        // 验证签名
        if (!$this->huifuService->verifyNotifySign($params)) {
            Log::error('汇付分账回调签名验证失败');
            return 'fail';
        }
        
        $orderNo = $params['out_trade_no'];
        $splitStatus = $params['split_status'];
        
        // 更新分账记录
        $splitRecord = OrderSplitRecord::where('order_no', $orderNo)->find();
        
        if ($splitRecord) {
            if ($splitStatus == 'SUCCESS') {
                $splitRecord->split_status = 2;
            } else {
                $splitRecord->split_status = 3;
                $splitRecord->error_msg = $params['fail_reason'] ?? '分账失败';
            }
            $splitRecord->split_result = json_encode($params);
            $splitRecord->update_time = time();
            $splitRecord->save();
        }
        
        return 'success';
    }
    
    /**
     * 处理支付成功订单
     */
    private function processPaidOrder($orderNo, $tradeNo, $params)
    {
        try {
            $order = Order::where('order_no', $orderNo)->find();
            
            if (!$order) {
                Log::error('订单不存在:' . $orderNo);
                return false;
            }
            
            if ($order['pay_status'] == 1) {
                return true; // 已处理过
            }
            
            // 更新订单支付状态
            $order->pay_status = 1;
            $order->pay_time = time();
            $order->trade_no = $tradeNo;
            $order->pay_info = json_encode($params);
            $order->save();
            
            // 触发支付成功事件
            event('OrderPaid', $order);
            
            // 处理分账
            if ($order['store_id'] > 0) {
                // 延迟处理分账,等待订单完成
                $this->splitService->processOrderSplit($order['id']);
            }
            
            return true;
            
        } catch (\Exception $e) {
            Log::error('处理支付成功订单失败:' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * 退款申请
     */
    public function refund(Request $request)
    {
        $orderNo = $request->param('order_no');
        $refundAmount = $request->param('refund_amount');
        $refundReason = $request->param('refund_reason');
        
        $order = Order::where('order_no', $orderNo)->find();
        
        if (!$order) {
            return json(['code' => 1, 'msg' => '订单不存在']);
        }
        
        if ($order['pay_status'] != 1) {
            return json(['code' => 1, 'msg' => '订单未支付']);
        }
        
        if ($refundAmount > $order['pay_amount']) {
            return json(['code' => 1, 'msg' => '退款金额不能大于支付金额']);
        }
        
        // 生成退款单号
        $refundNo = 'REFUND' . date('YmdHis') . rand(1000, 9999);
        
        // 调用汇付退款接口
        $response = $this->huifuService->refund(
            $orderNo,
            $refundNo,
            $refundAmount,
            $order['pay_amount']
        );
        
        if ($response['code'] == 'SUCCESS') {
            // 更新订单退款状态
            $order->refund_status = 1;
            $order->refund_amount = $refundAmount;
            $order->refund_time = time();
            $order->save();
            
            // 处理退款分账
            $this->splitService->processRefundSplit($order['id'], $refundAmount);
            
            return json(['code' => 0, 'msg' => '退款申请成功']);
        } else {
            return json(['code' => 1, 'msg' => '退款失败:' . ($response['msg'] ?? '')]);
        }
    }
    
    /**
     * 查询订单
     */
    public function queryOrder(Request $request)
    {
        $orderNo = $request->param('order_no');
        
        $response = $this->huifuService->queryOrder($orderNo);
        
        return json($response);
    }
}

7. 门店控制器


<?php
// app/controller/StoreController.php

namespace app\controller;

use think\Request;
use app\BaseController;
use app\model\Store;
use app\service\StoreAccountService;
use app\service\SplitService;

class StoreController extends BaseController
{
    private $storeAccountService;
    private $splitService;
    
    public function __construct()
    {
        $this->storeAccountService = new StoreAccountService();
        $this->splitService = new SplitService();
    }
    
    /**
     * 创建门店分账账户
     */
    public function createSplitAccount(Request $request)
    {
        $storeId = $request->param('store_id');
        $data = $request->only([
            'huifu_merchant_id',
            'split_ratio',
            'settlement_cycle',
            'bank_account_name',
            'bank_account_no',
            'bank_name',
            'bank_code',
        ]);
        
        try {
            $account = $this->storeAccountService->createStoreAccount($storeId, $data);
            
            return json([
                'code' => 0,
                'msg' => '创建成功',
                'data' => $account,
            ]);
            
        } catch (\Exception $e) {
            return json(['code' => 1, 'msg' => $e->getMessage()]);
        }
    }
    
    /**
     * 更新门店分账账户
     */
    public function updateSplitAccount(Request $request)
    {
        $storeId = $request->param('store_id');
        $data = $request->only([
            'huifu_merchant_id',
            'split_ratio',
            'settlement_cycle',
            'bank_account_name',
            'bank_account_no',
            'bank_name',
            'bank_code',
            'status',
        ]);
        
        try {
            $account = $this->storeAccountService->updateStoreAccount($storeId, $data);
            
            return json([
                'code' => 0,
                'msg' => '更新成功',
                'data' => $account,
            ]);
            
        } catch (\Exception $e) {
            return json(['code' => 1, 'msg' => $e->getMessage()]);
        }
    }
    
    /**
     * 获取门店分账账户
     */
    public function getSplitAccount(Request $request)
    {
        $storeId = $request->param('store_id');
        
        $account = \app\model\StoreSplitAccount::where('store_id', $storeId)->find();
        
        return json([
            'code' => 0,
            'data' => $account,
        ]);
    }
    
    /**
     * 门店分账统计
     */
    public function splitStats(Request $request)
    {
        $storeId = $request->param('store_id');
        $startDate = $request->param('start_date', date('Y-m-d', strtotime('-30 days')));
        $endDate = $request->param('end_date', date('Y-m-d'));
        
        $stats = $this->storeAccountService->getStoreSplitStats($storeId, $startDate, $endDate);
        
        return json([
            'code' => 0,
            'data' => $stats,
        ]);
    }
    
    /**
     * 批量导入门店分账账户
     */
    public function batchImportAccounts(Request $request)
    {
        $file = $request->file('file');
        
        if (!$file) {
            return json(['code' => 1, 'msg' => '请上传文件']);
        }
        
        // 解析Excel或CSV文件
        $accounts = $this->parseImportFile($file);
        
        if (empty($accounts)) {
            return json(['code' => 1, 'msg' => '文件内容为空']);
        }
        
        try {
            $result = $this->storeAccountService->batchImportAccounts($accounts);
            
            return json([
                'code' => 0,
                'msg' => '导入完成',
                'data' => $result,
            ]);
            
        } catch (\Exception $e) {
            return json(['code' => 1, 'msg' => $e->getMessage()]);
        }
    }
    
    /**
     * 解析导入文件(简化版)
     */
    private function parseImportFile($file)
    {
        // 实际项目中需要实现CSV/Excel解析
        // 这里仅作示例
        $content = file_get_contents($file->getPathname());
        $lines = explode("\n", $content);
        
        $accounts = [];
        $isFirst = true;
        
        foreach ($lines as $line) {
            if ($isFirst) {
                $isFirst = false;
                continue; // 跳过表头
            }
            
            $data = str_getcsv($line);
            if (count($data) >= 7) {
                $accounts[] = [
                    'store_id' => $data[0],
                    'huifu_merchant_id' => $data[1],
                    'split_ratio' => $data[2],
                    'bank_account_name' => $data[3],
                    'bank_account_no' => $data[4],
                    'bank_name' => $data[5],
                    'bank_code' => $data[6],
                ];
            }
        }
        
        return $accounts;
    }
}

8. 后台管理控制器


<?php
// app/controller/AdminController.php

namespace app\controller;

use think\Request;
use think\facade\Db;
use app\BaseController;
use app\model\Order;
use app\model\Store;
use app\model\OrderSplitRecord;
use app\model\StoreSplitAccount;
use app\model\StoreFeeRecord;
use app\service\SplitService;

class AdminController extends BaseController
{
    private $splitService;
    
    public function __construct()
    {
        $this->splitService = new SplitService();
    }
    
    /**
     * 分账记录列表
     */
    public function splitRecordList(Request $request)
    {
        $page = $request->param('page', 1);
        $limit = $request->param('limit', 20);
        $storeId = $request->param('store_id');
        $orderNo = $request->param('order_no');
        $status = $request->param('status');
        
        $query = OrderSplitRecord::with(['store']);
        
        if ($storeId) {
            $query->where('store_id', $storeId);
        }
        
        if ($orderNo) {
            $query->where('order_no', 'like', '%' . $orderNo . '%');
        }
        
        if ($status !== '') {
            $query->where('split_status', $status);
        }
        
        $list = $query->order('id', 'desc')
            ->paginate($limit, false, ['page' => $page]);
            
        return json([
            'code' => 0,
            'data' => $list->items(),
            'total' => $list->total(),
        ]);
    }
    
    /**
     * 分账详情
     */
    public function splitDetail(Request $request)
    {
        $id = $request->param('id');
        
        $record = OrderSplitRecord::with(['store'])->find($id);
        
        if (!$record) {
            return json(['code' => 1, 'msg' => '记录不存在']);
        }
        
        return json([
            'code' => 0,
            'data' => $record,
        ]);
    }
    
    /**
     * 手动执行分账
     */
    public function manualSplit(Request $request)
    {
        $orderId = $request->param('order_id');
        
        $order = Order::with('orderGoods')->find($orderId);
        
        if (!$order) {
            return json(['code' => 1, 'msg' => '订单不存在']);
        }
        
        // 调用分账服务
        $result = $this->splitService->processOrderSplit($orderId);
        
        if ($result) {
            return json(['code' => 0, 'msg' => '分账处理成功']);
        } else {
            return json(['code' => 1, 'msg' => '分账处理失败']);
        }
    }
    
    /**
     * 分账重试
     */
    public function retrySplit(Request $request)
    {
        $id = $request->param('id');
        
        $record = OrderSplitRecord::find($id);
        
        if (!$record) {
            return json(['code' => 1, 'msg' => '记录不存在']);
        }
        
        if ($record['split_status'] != 3) {
            return json(['code' => 1, 'msg' => '只有失败状态可以重试']);
        }
        
        $order = Order::find($record['order_id']);
        
        if (!$order) {
            return json(['code' => 1, 'msg' => '订单不存在']);
        }
        
        // 获取门店分账配置
        $storeSplits = $this->splitService->getStoreSplits($order['order_goods']);
        
        // 构建分账列表
        $splitList = $this->splitService->calculateStoreSplit($order, $storeSplits);
        
        // 执行分账
        $result = $this->splitService->executeSplit($order, $splitList, $record);
        
        if ($result) {
            return json(['code' => 0, 'msg' => '重试成功']);
        } else {
            return json(['code' => 1, 'msg' => '重试失败']);
        }
    }
    
    /**
     * 门店手续费统计
     */
    public function storeFeeStats(Request $request)
    {
        $storeId = $request->param('store_id');
        $startDate = $request->param('start_date', date('Y-m-d', strtotime('-30 days')));
        $endDate = $request->param('end_date', date('Y-m-d'));
        $page = $request->param('page', 1);
        $limit = $request->param('limit', 20);
        
        $query = StoreFeeRecord::with(['store']);
        
        if ($storeId) {
            $query->where('store_id', $storeId);
        }
        
        if ($startDate) {
            $query->where('create_time', '>=', strtotime($startDate));
        }
        
        if ($endDate) {
            $query->where('create_time', '<=', strtotime($endDate . ' 23:59:59'));
        }
        
        $list = $query->order('id', 'desc')
            ->paginate($limit, false, ['page' => $page]);
            
        // 统计合计
        $total = Db::name('store_fee_record')
            ->where($query->getOptions('where'))
            ->sum('fee_amount');
            
        return json([
            'code' => 0,
            'data' => $list->items(),
            'total' => $list->total(),
            'total_fee' => $total,
        ]);
    }
    
    /**
     * 平台分账总览
     */
    public function platformSplitOverview(Request $request)
    {
        $date = $request->param('date', date('Y-m-d'));
        
        $today = strtotime($date);
        $tomorrow = $today + 86400;
        
        // 今日分账统计
        $todayStats = Db::name('order_split_record')
            ->whereBetweenTime('create_time', $today, $tomorrow)
            ->field([
                'COUNT(*) as total_orders',
                'SUM(split_amount) as total_split',
                'SUM(platform_fee) as total_fee',
                'SUM(CASE WHEN split_status = 2 THEN split_amount ELSE 0 END) as success_amount',
                'COUNT(CASE WHEN split_status = 2 THEN 1 END) as success_count',
                'COUNT(CASE WHEN split_status = 3 THEN 1 END) as fail_count',
            ])
            ->find();
            
        // 本月统计
        $monthStart = strtotime(date('Y-m-01', $today));
        $monthStats = Db::name('order_split_record')
            ->where('create_time', '>=', $monthStart)
            ->field([
                'SUM(split_amount) as total_split',
                'SUM(platform_fee) as total_fee',
            ])
            ->find();
            
        // 待分账订单
        $pendingCount = OrderSplitRecord::where('split_status', 0)
            ->where('create_time', '<=', time() - 3600)
            ->count();
            
        // 分账失败订单
        $failCount = OrderSplitRecord::where('split_status', 3)->count();
        
        return json([
            'code' => 0,
            'data' => [
                'today' => $todayStats,
                'month' => $monthStats,
                'pending_count' => $pendingCount,
                'fail_count' => $failCount,
            ],
        ]);
    }
}

9. 命令行任务


<?php
// app/command/SplitTask.php

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Log;
use think\facade\Cache;
use app\model\Order;
use app\model\OrderSplitRecord;
use app\service\SplitService;

class SplitTask extends Command
{
    protected function configure()
    {
        $this->setName('split:process')
            ->setDescription('处理待分账订单');
    }
    
    protected function execute(Input $input, Output $output)
    {
        $output->writeln('开始处理待分账订单...');
        
        try {
            $splitService = new SplitService();
            
            // 处理延迟分账队列
            $this->processDelayQueue($splitService);
            
            // 处理失败重试队列
            $this->processRetryQueue($splitService);
            
            // 处理超时未分账订单
            $this->processTimeoutOrders($splitService);
            
            $output->writeln('处理完成');
            
        } catch (\Exception $e) {
            $output->writeln('处理失败:' . $e->getMessage());
            Log::error('分账任务执行失败:' . $e->getMessage());
        }
    }
    
    /**
     * 处理延迟分账队列
     */
    private function processDelayQueue($splitService)
    {
        $keys = Cache::get('split_queue_keys', []);
        $now = time();
        
        foreach ($keys as $key) {
            $data = Cache::get($key);
            if ($data && $data['execute_time'] <= $now) {
                $orderId = $data['order_id'];
                
                $order = Order::with('orderGoods')->find($orderId);
                if ($order) {
                    $splitService->processOrderSplit($orderId);
                }
                
                Cache::delete($key);
            }
        }
    }
    
    /**
     * 处理失败重试队列
     */
    private function processRetryQueue($splitService)
    {
        $keys = Cache::get('split_retry_keys', []);
        $now = time();
        
        foreach ($keys as $key) {
            $data = Cache::get($key);
            if ($data && $data['execute_time'] <= $now) {
                $orderId = $data['order_id'];
                
                $record = OrderSplitRecord::where('order_id', $orderId)
                    ->where('split_status', 3)
                    ->find();
                    
                if ($record) {
                    $order = Order::with('orderGoods')->find($orderId);
                    
                    if ($order) {
                        // 获取门店分账配置
                        $storeSplits = $splitService->getStoreSplits($order['order_goods']);
                        $splitList = $splitService->calculateStoreSplit($order, $storeSplits);
                        
                        $splitService->executeSplit($order, $splitList, $record);
                    }
                }
                
                Cache::delete($key);
            }
        }
    }
    
    /**
     * 处理超时未分账订单
     */
    private function processTimeoutOrders($splitService)
    {
        // 查找超过24小时未分账的订单
        $timeout = time() - 86400;
        
        $records = OrderSplitRecord::where('split_status', 0)
            ->where('create_time', '<=', $timeout)
            ->select();
            
        foreach ($records as $record) {
            $order = Order::with('orderGoods')->find($record['order_id']);
            
            if ($order) {
                $splitService->processOrderSplit($order['id']);
            }
        }
    }
}

10. 常用辅助函数


<?php
// app/common.php

use think\facade\Log;

/**
 * 记录分账日志
 */
function log_split($message, $data = [])
{
    $log = [
        'time' => date('Y-m-d H:i:s'),
        'message' => $message,
        'data' => $data,
    ];
    
    Log::channel('split')->info(json_encode($log, JSON_UNESCAPED_UNICODE));
}

/**
 * 生成唯一订单号
 */
function generate_order_no($prefix = '')
{
    return $prefix . date('YmdHis') . rand(1000, 9999);
}

/**
 * 金额分转元
 */
function amount_fen_to_yuan($amount)
{
    return number_format($amount / 100, 2, '.', '');
}

/**
 * 金额元转分
 */
function amount_yuan_to_fen($amount)
{
    return intval(bcmul($amount, 100, 0));
}

/**
 * 计算分账金额
 */
function calculate_split_amount($total, $ratio)
{
    return bcmul($total, bcdiv($ratio, 100, 4), 2);
}

11. 数据库迁移执行


# 执行数据库迁移
php think migrate:run

12. 添加路由配置


<?php
// route/app.php

use think\facade\Route;

// 支付相关路由
Route::group('payment', function () {
    Route::post('unifiedOrder', 'PaymentController/unifiedOrder');
    Route::post('notify', 'PaymentController/notify')->withoutMiddleware();
    Route::post('splitNotify', 'PaymentController/splitNotify')->withoutMiddleware();
    Route::post('refund', 'PaymentController/refund');
    Route::get('query', 'PaymentController/queryOrder');
});

// 门店分账相关路由
Route::group('store', function () {
    Route::post('splitAccount/create', 'StoreController/createSplitAccount');
    Route::post('splitAccount/update', 'StoreController/updateSplitAccount');
    Route::get('splitAccount/get', 'StoreController/getSplitAccount');
    Route::get('splitStats', 'StoreController/splitStats');
    Route::post('splitAccount/batchImport', 'StoreController/batchImportAccounts');
})->middleware('Auth');

// 后台管理路由
Route::group('admin', function () {
    Route::get('splitRecord/list', 'AdminController/splitRecordList');
    Route::get('splitRecord/detail', 'AdminController/splitDetail');
    Route::post('split/manual', 'AdminController/manualSplit');
    Route::post('split/retry', 'AdminController/retrySplit');
    Route::get('fee/stats', 'AdminController/storeFeeStats');
    Route::get('split/overview', 'AdminController/platformSplitOverview');
})->middleware('AdminAuth');

13. 配置文件添加日志通道


<?php
// config/log.php

return [
    'default' => env('LOG_CHANNEL', 'file'),
    
    'channels' => [
        'file' => [
            'type' => 'file',
            'path' => runtime_path() . '/log/app.log',
            'level' => ['debug', 'info', 'error'],
        ],
        'split' => [
            'type' => 'file',
            'path' => runtime_path() . '/log/split.log',
            'level' => ['info', 'error'],
        ],
        'huifu' => [
            'type' => 'file',
            'path' => runtime_path() . '/log/huifu.log',
            'level' => ['info', 'error'],
        ],
    ],
];

使用说明

  1. 安装依赖:确保ThinkPHP 6.0+环境
  2. 配置参数:在.env文件中配置汇付支付相关参数
  3. 执行迁移:运行数据库迁移创建相关表
  4. 配置定时任务:设置cron执行分账任务
  5. 测试对接:使用汇付支付测试环境进行联调​


{{voteData.voteSum}} 人已参与
支持
反对
请登录后查看

分账汇付斗拱定制开发对接 最后编辑于2026-03-05 08:55:06

快捷回复
回复
回复
回复({{post_count}}) {{!is_user ? '我的回复' :'全部回复'}}
排序 默认正序 回复倒序 点赞倒序

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level || item.bbs_level }}

作者 管理员 企业

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推荐': '推荐'}}
{{item.is_suggest == 1? '取消推荐': '推荐'}} 【已收集】
{{item.floor}}# 沙发 板凳 地板 {{item.floor}}# 【已收集】
{{item.user_info.title || '暂无简介'}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
打赏
已打赏¥{{item.reward_price}}
{{item.like_count}}
分享
{{item.showReply ? '取消回复' : '回复'}}
删除
回复
回复

{{itemc.user_info.nickname}}

{{itemc.user_name}}

回复 {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}
打赏
已打赏¥{{itemc.reward_price}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回复' : '回复'}}
删除
回复
回复
收起 展开更多
查看更多
打赏
已打赏¥{{reward_price}}
63
{{like_count}}
{{collect_count}}
添加回复 ({{post_count}})

相关推荐

快速安全登录

使用微信扫码登录
回复
回复
问题:
问题自动获取的帖子内容,不准确时需要手动修改. [获取答案]
答案:
提交
bug 需求 取 消 确 定
打赏金额
当前余额:¥{{rewardUserInfo.reward_price}}
{{item.price}}元
请输入 0.1-{{reward_max_price}} 范围内的数值
打赏成功
¥{{price}}
完成 确认打赏

微信登录/注册

切换手机号登录

{{ bind_phone ? '绑定手机' : '手机登录'}}

{{codeText}}
切换微信登录/注册
暂不绑定
CRMEB客服
CRMEB咨询热线 400-8888-794

扫码领取产品资料

功能清单
思维导图
安装教程
CRMEB开源商城下载 源码下载 CRMEB帮助文档 帮助文档
返回顶部 返回顶部
CRMEB客服