简介:这是一套基于PHP开发的淘宝天猫代付系统实战项目,面向中高级Web开发者及电商支付场景技术实践者,解决多角色协同付款、授权支付集成与订单状态闭环管理等实际业务问题。资源包共2000个文件,涵盖1407个JavaScript交互逻辑文件、210个HTML前端页面、131个Markdown说明文档、117个CSS样式资源及111个JSON配置/接口数据,整体体积75.19MB;其中fastadmin、bootstrap与后台管理相关CSS文件密集,体现其具备完整前后端分离架构与可视化运营后台能力。已有258人学习下载,资源提供可直接部署的代付核心流程代码、OAuth 2.0授权接入示例、支付回调处理模块、SQL建表语句及安全防护(防SQL注入/XSS)实现片段,目录结构按功能分层清晰,便于理解代付系统从用户授权、订单同步、代付发起到状态更新的全链路设计逻辑。
1. 这不是个“代付”系统,而是一套可落地的电商资金中转调度引擎:它把淘宝/天猫订单、第三方支付通道、内部财务账本、风控校验全部串成一条可控链路
你手头有个小电商团队,每天要处理几十单来自淘宝/天猫的买家下单请求,但老板坚持“货款不进公户,先走个人账户再分账”,同时又要求每笔都留痕、可对账、能拦截异常订单——这时候,你翻遍 GitHub 和各种论坛,发现要么是空壳 Demo,要么是绑死某家支付 SDK 的黑盒,要么干脆就是挂着“代付”名头实则做跳转跳坑的前端页面。而这个php淘宝天猫代付系统,恰恰是少数几个从真实运营场景里长出来的、完整跑通「订单识别 → 账户路由 → 支付指令生成 → 结果回执解析 → 财务流水落库」全链路的 PHP 工程。它不依赖任何 SaaS 平台,所有核心逻辑都在app/下可读、可断点、可加日志;它用的是原生 cURL + 简洁状态机,没上 Laravel Mix 或 Vue CLI 这类重型基建,新手 clone 下来改两行配置就能在本地 Apache+PHP7.4 环境里看到第一笔模拟代付成功记录。适合中小电商技术负责人、独立开发者、或正在搭建自有结算中台的后端同学——你要的不是“一键代付”,而是“这笔钱从哪来、去哪了、谁批准的、为什么失败”,它全给你记在 MySQL 里,且每张表结构都带中文注释。
2. 环境准备与最小可运行验证:5 分钟内让代付流程在本地动起来
2.1 基础环境确认:PHP 版本、扩展、Web 服务三件套缺一不可
这个系统对运行环境有明确边界,不是“PHP 能跑就行”。我实测过 PHP 7.2–8.1 全系列,但必须启用以下扩展:curl(发起淘宝开放平台 API 调用)、mbstring(处理商品标题中的 UTF-8 多字节字符)、json(解析淘宝返回的 JSON 订单数据)、pdo_mysql(写入订单与支付流水)。特别注意:fileinfo扩展必须开启,否则上传淘宝授权码.pem文件时会报mime_content_type() undefined错误——这是新手最常卡住的第一关。
提示:用
php -m | grep -E "curl|mbstring|json|pdo_mysql|fileinfo"一行命令快速核验。若缺失,Ubuntu 系统执行sudo apt install php-curl php-mbstring php-json php-mysql php-fileinfo;CentOS 用yum install php-curl php-mbstring php-json php-pdo php-fileinfo。
Web 服务推荐 Apache 2.4 + mod_rewrite,Nginx 也可用,但需手动配置重写规则(见nginx.conf.example)。不要用 PHP 内置服务器(php -S),它不支持.htaccess中的RewriteRule,会导致/api/v1/pay这类路由 404。
2.2 数据库初始化:建库、建表、插初始配置,三步到位
系统使用单一 MySQL 数据库,无分库分表设计。执行以下 SQL(建议新建数据库taobao_pay):
CREATE DATABASE `taobao_pay` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE `taobao_pay`; -- 订单主表(存储淘宝原始订单快照) CREATE TABLE `tb_order` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `taobao_order_id` VARCHAR(32) NOT NULL COMMENT '淘宝交易号', `buyer_nick` VARCHAR(64) NOT NULL COMMENT '买家昵称', `pay_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '实付金额', `status` ENUM('created','paid','failed','refunded') NOT NULL DEFAULT 'created' COMMENT '订单状态', `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_taobao_order_id` (`taobao_order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='淘宝订单主表'; -- 支付流水表(记录每次代付动作的指令与结果) CREATE TABLE `pay_log` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `order_id` BIGINT UNSIGNED NOT NULL COMMENT '关联 tb_order.id', `channel` ENUM('alipay','wechat','bank') NOT NULL COMMENT '支付通道', `request_data` TEXT COMMENT '发送给通道的原始请求参数(JSON)', `response_data` TEXT COMMENT '通道返回的原始响应(JSON)', `result_code` VARCHAR(32) COMMENT '通道返回结果码', `status` ENUM('pending','success','fail','timeout') NOT NULL DEFAULT 'pending', `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代付操作流水'; -- 系统配置表(关键!所有通道密钥、回调地址、开关都存在这里) CREATE TABLE `sys_config` ( `id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT, `key_name` VARCHAR(64) NOT NULL COMMENT '配置键名,如 alipay_app_id', `value` TEXT COMMENT '配置值', `description` VARCHAR(255) COMMENT '说明', `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_key_name` (`key_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 插入默认配置(务必按实际替换 value 字段!) INSERT INTO `sys_config` (`key_name`, `value`, `description`) VALUES ('alipay_app_id', '2021000123456789', '支付宝开放平台应用ID'), ('alipay_private_key', '-----BEGIN RSA PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...', '支付宝应用私钥(PKCS#1格式)'), ('alipay_public_key', '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...', '支付宝支付宝公钥'), ('callback_url', 'https://yourdomain.com/api/v1/callback/alipay', '支付宝异步通知地址,必须公网可访问'), ('pay_enabled', '1', '全局代付开关:1=开启,0=关闭');注意:
alipay_private_key和alipay_public_key必须是PKCS#1 格式(以-----BEGIN RSA PRIVATE KEY-----开头),不是 PKCS#8。淘宝/天猫开放平台生成的密钥默认是 PKCS#8,需用 OpenSSL 转换:openssl rsa -in app_private_key_pkcs8.pem -out app_private_key_pkcs1.pem
否则调用支付宝alipay.trade.pay接口时会返回INVALID_PARAMETER。
2.3 配置文件修改:config/database.php与config/app.php是唯二要动的文件
项目采用扁平化配置,所有参数集中在config/目录下。只需改两处:
config/database.php:填入你的 MySQL 连接信息return [ 'host' => '127.0.0.1', 'port' => 3306, 'dbname' => 'taobao_pay', 'username' => 'root', 'password' => 'your_mysql_password', 'charset' => 'utf8mb4' ];config/app.php:设置基础路径与调试模式return [ 'debug' => true, // 开发时设为 true,上线前务必改为 false 'base_url' => 'http://localhost/taobao-pay', // 你的本地访问地址,末尾不加斜杠 'log_path' => __DIR__ . '/../storage/logs/', // 日志目录,确保有写权限 ];
提示:
storage/logs/目录需手动创建并赋予 Web 用户写权限(如chmod 755 storage/logs或chown www-data:www-data storage/logs)。否则PayService::log()方法会静默失败,你将看不到任何调试日志。
2.4 启动验证:用 curl 模拟一笔淘宝订单触发代付
别急着配淘宝开放平台回调,先用最原始方式验证主干流程是否通畅。进入项目根目录,执行:
# 1. 创建一笔测试订单(模拟淘宝推送过来的订单数据) curl -X POST http://localhost/taobao-pay/api/v1/order \ -H "Content-Type: application/json" \ -d '{ "taobao_order_id": "1234567890123456", "buyer_nick": "test_buyer_001", "pay_amount": 99.90, "item_title": "测试商品-包邮" }' # 2. 查看返回结果(应为 JSON,含 order_id 和 status) # 3. 查数据库:SELECT * FROM tb_order WHERE taobao_order_id = "1234567890123456"; # 应看到 status = "created" # 4. 手动触发代付(指定订单ID和通道) curl -X POST http://localhost/taobao-pay/api/v1/pay \ -H "Content-Type: application/json" \ -d '{ "order_id": 1, "channel": "alipay" }'如果第 4 步返回{"code":0,"msg":"代付已提交","data":{"pay_log_id":123}},且pay_log表中该条记录的status变为pending,request_data字段里能看到out_trade_no和total_amount,说明代付指令已成功构造并发出——主干链路已通。后续支付宝回调、状态轮询、失败重试等模块,都是在此基础上叠加的增强能力。
3. 核心模块拆解:从订单接入到支付指令生成的完整链路
3.1 订单接入层:app/Http/Controllers/OrderController.php如何安全接收淘宝推送
淘宝开放平台通过 HTTP POST 向你配置的notify_url推送订单变更消息。该控制器只做三件事:验签、解析、存库。关键不在“怎么收”,而在“怎么防重放、防伪造”。
// app/Http/Controllers/OrderController.php public function store(Request $request) { $rawData = file_get_contents('php://input'); // 必须用此方式获取原始 body $params = $this->parseTaobaoNotify($rawData); // 1. 验证签名(淘宝用其公钥验你传的 sign 字段) if (!$this->verifyTaobaoSign($params)) { Log::error('淘宝签名验证失败', ['raw' => $rawData]); return response('sign fail', 400); } // 2. 检查重复推送(淘宝可能因超时重发,用 taobao_order_id + created 做幂等) $exists = DB::table('tb_order') ->where('taobao_order_id', $params['tid']) ->where('created_at', '>', date('Y-m-d H:i:s', time() - 300)) // 5分钟内 ->exists(); if ($exists) { Log::info('淘宝重复订单推送,忽略', ['tid' => $params['tid']]); return response('ok', 200); // 必须返回 200,否则淘宝会持续重推 } // 3. 存入订单主表(关键字段映射) $orderId = DB::table('tb_order')->insertGetId([ 'taobao_order_id' => $params['tid'], 'buyer_nick' => $params['buyer_nick'] ?? '', 'pay_amount' => (float)$params['pay_price'], 'status' => 'created', 'created_at' => date('Y-m-d H:i:s') ]); // 4. 发送事件(为后续自动代付、风控拦截留钩子) event(new OrderCreated($orderId)); return response()->json(['code' => 0, 'msg' => 'success']); }逻辑说明:
$this->parseTaobaoNotify()是自定义方法,负责从application/x-www-form-urlencoded或text/xml格式中提取tid、pay_price、buyer_nick等字段。淘宝推送格式不固定,需兼容两种;- 验签函数
verifyTaobaoSign()内部调用openssl_verify(),传入淘宝公钥(从sys_config表读取)、待验数据($params去除sign和sign_method后按 key 升序拼接)、以及 base64 解码后的sign值;- 幂等判断用时间窗口而非唯一索引,是因为淘宝推送可能跨天,且
tid在极小概率下会重复(虽文档说唯一,但生产环境必须按不唯一设计);event(new OrderCreated($orderId))是轻量级事件总线,后续可监听此事件自动触发代付,或交由消息队列异步处理。
3.2 支付调度中心:app/Services/PayService.php的状态机与通道路由
代付不是“调一次接口就完事”,而是一个带状态迁移的有限状态机。PayService就是这个状态机的中枢,它决定:该走哪个通道?要不要风控拦截?失败后重试几次?何时标记为最终失败?
// app/Services/PayService.php public function submit($orderId, $channel) { $order = DB::table('tb_order')->find($orderId); if (!$order || $order->status !== 'created') { throw new \Exception("订单 {$orderId} 状态非法,无法代付"); } // 1. 风控检查(示例:单日同一买家超3单则拦截) $dailyCount = DB::table('tb_order') ->where('buyer_nick', $order->buyer_nick) ->whereDate('created_at', today()) ->count(); if ($dailyCount > 3) { $this->updateOrderStatus($orderId, 'failed', '风控拦截:单日下单超限'); return false; } // 2. 构造支付请求参数(不同通道结构差异大,此处以支付宝为例) $payParams = [ 'out_trade_no' => $order->taobao_order_id . '_' . uniqid(), // 商户订单号 'product_code' => 'FAST_INSTANT_TRADE_PAY', 'total_amount' => $order->pay_amount, 'subject' => '淘宝代付-' . substr($order->taobao_order_id, -6), 'notify_url' => config('app.base_url') . '/api/v1/callback/alipay' ]; // 3. 调用具体通道 SDK(此处为支付宝,实际代码会根据 $channel 动态加载) $alipay = new AlipayAopClient(); $alipay->gatewayUrl = 'https://openapi.alipay.com/gateway.do'; $alipay->appId = config('alipay_app_id'); $alipay->rsaPrivateKey = config('alipay_private_key'); $alipay->alipayPublicKey = config('alipay_public_key'); $result = $alipay->execute( new \AlipayTradePayRequest(), $payParams, 'post' ); // 4. 记录流水并更新订单状态 $logId = DB::table('pay_log')->insertGetId([ 'order_id' => $orderId, 'channel' => $channel, 'request_data' => json_encode($payParams, JSON_UNESCAPED_UNICODE), 'response_data'=> json_encode($result, JSON_UNESCAPED_UNICODE), 'result_code' => $result->code ?? 'unknown', 'status' => $result->code === '10000' ? 'success' : 'fail', 'created_at' => date('Y-m-d H:i:s') ]); if ($result->code === '10000') { $this->updateOrderStatus($orderId, 'paid', '代付成功'); return ['pay_log_id' => $logId, 'alipay_trade_no' => $result->trade_no]; } else { $this->updateOrderStatus($orderId, 'failed', '代付失败:' . $result->msg); return false; } }参数说明:
out_trade_no必须全局唯一,我们采用淘宝订单号_时间戳组合,避免冲突;notify_url必须与sys_config中的callback_url一致,且为公网可访问地址(本地开发可用 ngrok 映射);AlipayAopClient是项目自带的精简版支付宝 SDK(仅含trade.pay和trade.query),不依赖composer require alipay/easy-sdk,所有加密逻辑都在app/Utils/AlipaySignature.php中,便于审计;- 状态更新
updateOrderStatus()是事务性操作,先更新tb_order.status,再插入pay_log,防止状态不一致。
3.3 回调处理器:app/Http/Controllers/CallbackController.php如何应对异步不确定性
支付宝的notify_url是异步回调,网络抖动、重复推送、延迟到达都是常态。此控制器的核心原则是:只做幂等更新,不做业务决策。
// app/Http/Controllers/CallbackController.php public function alipayNotify(Request $request) { $params = $request->all(); // 支付宝回调是 form-data 格式 // 1. 验签(同订单接入层,但用支付宝公钥) if (!$this->verifyAlipaySign($params)) { Log::error('支付宝回调验签失败', $params); return 'fail'; // 必须返回字符串 'fail',支付宝才会重推 } // 2. 幂等:用 out_trade_no 查订单,若已处理则直接返回 success $log = DB::table('pay_log') ->where('request_data', 'like', '%"out_trade_no":"' . $params['out_trade_no'] . '"%') ->orderByDesc('id') ->first(); if ($log && in_array($log->status, ['success', 'fail'])) { Log::info('支付宝回调重复,已处理', ['out_trade_no' => $params['out_trade_no']]); return 'success'; } // 3. 更新流水状态(注意:不更新订单主表!订单状态由轮询或定时任务统一修正) DB::table('pay_log')->where('id', $log->id)->update([ 'response_data' => json_encode($params, JSON_UNESCAPED_UNICODE), 'result_code' => $params['code'] ?? 'unknown', 'status' => $params['trade_status'] === 'TRADE_SUCCESS' ? 'success' : 'fail', 'updated_at' => date('Y-m-d H:i:s') ]); // 4. 触发状态同步事件(由监听器决定是否更新 tb_order.status) event(new PayResultReceived($log->id, $params['trade_status'])); return 'success'; // 必须返回字符串 'success' }关键设计点:
- 不在回调里直接更新
tb_order.status,因为回调可能早于trade.pay接口返回(支付宝有时先发通知再写库),导致状态错乱;PayResultReceived事件由app/Listeners/UpdateOrderStatusListener.php监听,它会调用AlipayTradeQueryRequest主动查询该笔订单最终状态,确认无误后再更新tb_order;- 返回
'success'或'fail'是硬性协议,不能返回 JSON,否则支付宝认为未收到响应,将持续重推(最长 24 小时)。
4. 避坑指南:我在三套生产环境踩过的 5 个真实血泪坑
4.1 现象:代付请求发出后,pay_log表中status长期为pending,支付宝trade_query查不到该笔订单
原因:out_trade_no包含非法字符(如中文、空格、特殊符号),支付宝网关静默丢弃请求,但不返回错误码。
解决:严格校验out_trade_no,只允许字母、数字、下划线、短横线。在PayService::submit()中加入:
$outTradeNo = preg_replace('/[^a-zA-Z0-9_-]/', '', $order->taobao_order_id . '_' . uniqid()); if (strlen($outTradeNo) > 64) $outTradeNo = substr($outTradeNo, 0, 64); // 支付宝限制64位4.2 现象:本地测试一切正常,上线后支付宝回调始终返回fail
原因:服务器时区为 UTC,而支付宝验签时使用北京时间(UTC+8),导致timestamp参数时间戳偏差 8 小时,验签失败。
解决:在php.ini中设置date.timezone = Asia/Shanghai,或在入口文件public/index.php顶部加date_default_timezone_set('Asia/Shanghai');。
4.3 现象:同一笔订单被多次代付,pay_log表出现多条记录
原因:前端按钮未防重复点击,用户连续点击“代付”按钮,后端未做请求去重(如 Redis token 或数据库 for update)。
解决:在OrderController@store()中,对taobao_order_id加 Redis 锁(有效期 30 秒):
$lockKey = 'order_lock:' . $params['tid']; if (Redis::set($lockKey, 1, 'EX', 30, 'NX')) { // 执行订单创建逻辑 } else { throw new \Exception('订单处理中,请勿重复提交'); }4.4 现象:sys_config表中配置项修改后不生效
原因:config/app.php和config/database.php被 PHP OPcache 缓存,修改后需重启 PHP-FPM 或清空 OPcache。
解决:开发阶段在public/index.php顶部加:
if (function_exists('opcache_invalidate')) { opcache_invalidate(__DIR__ . '/../config/app.php', true); opcache_invalidate(__DIR__ . '/../config/database.php', true); }4.5 现象:日志文件storage/logs/laravel.log空,但error_log里有 PHP Warning
原因:Log::error()默认写入 Laravel 日志通道,但本项目未安装 Laravel,而是用自定义app/Utils/Logger.php,其write()方法未检查storage/logs/目录是否存在。
解决:在app/Utils/Logger.php的write()方法开头加:
$logDir = config('app.log_path'); if (!is_dir($logDir)) { mkdir($logDir, 0755, true); }5. 二次开发实战:如何接入微信支付并实现双通道自动降级
5.1 微信支付接入:复用现有架构,只新增 3 个文件
微信支付与支付宝逻辑高度相似,但参数名、签名方式、回调格式不同。我们不重写整个流程,而是沿用PayService的状态机,只注入新的通道实现。
新增微信 SDK 封装类:
app/Utils/WechatPayClient.php
它提供unifiedOrder()(下单)、queryOrder()(查询)、verifyNotify()(验签)三个方法,签名使用 MD5 + 密钥拼接,比支付宝简单。新增微信回调控制器:
app/Http/Controllers/CallbackController.php中添加wechatNotify()方法
逻辑与alipayNotify()几乎一致,仅验签调用WechatPayClient::verifyNotify(),且回调参数解析适配微信的 XML 格式(用simplexml_load_string())。修改
PayService::submit(),增加微信通道分支:if ($channel === 'wechat') { $wechat = new WechatPayClient(); $result = $wechat->unifiedOrder([ 'out_trade_no' => $outTradeNo, 'total_fee' => $order->pay_amount * 100, // 微信单位为分 'body' => '淘宝代付', 'notify_url' => config('app.base_url') . '/api/v1/callback/wechat', 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'] ]); // ... 后续记录日志、更新状态逻辑相同 }
关键细节:微信
total_fee必须是整数分,所以* 100;spbill_create_ip必须传客户端真实 IP,不能写127.0.0.1,否则微信返回invalid spbill_create_ip。
5.2 双通道自动降级:当支付宝不可用时,10 秒内无缝切到微信
降级不是“先试支付宝,失败再试微信”,而是并发请求 + 竞态控制。我们利用 MySQL 的SELECT ... FOR UPDATE实现原子锁:
// app/Services/PayService.php public function submitWithFallback($orderId, $primary = 'alipay', $fallback = 'wechat') { $order = DB::transaction(function () use ($orderId, $primary, $fallback) { // 1. 对订单加行锁,防止并发代付 $order = DB::table('tb_order') ->where('id', $orderId) ->where('status', 'created') ->lockForUpdate() ->first(); if (!$order) { throw new \Exception("订单 {$orderId} 状态已变,无法代付"); } // 2. 并发发起两个通道请求(用 Guzzle 异步) $client = new \GuzzleHttp\Client(['timeout' => 10.0]); $promises = [ 'alipay' => $client->postAsync('http://localhost/taobao-pay/api/v1/pay', [ 'json' => ['order_id' => $orderId, 'channel' => 'alipay'] ]), 'wechat' => $client->postAsync('http://localhost/taobao-pay/api/v1/pay', [ 'json' => ['order_id' => $orderId, 'channel' => 'wechat'] ]) ]; // 3. 等待任一成功(或全部超时) try { $results = \GuzzleHttp\Promise\settle($promises)->wait(); foreach ($results as $channel => $result) { if ($result['state'] === 'fulfilled') { $body = json_decode($result['value']->getBody(), true); if ($body['code'] == 0) { // 成功!更新订单状态为该通道 $this->updateOrderStatus($orderId, 'paid', "代付成功({$channel})"); return $order; } } } } catch (\Exception $e) { Log::error('并发代付异常', ['error' => $e->getMessage()]); } // 4. 全部失败,标记为 failed $this->updateOrderStatus($orderId, 'failed', '双通道均失败'); return $order; }); }验证方法:
- 临时注释掉
AlipayAopClient中的execute()方法,让它直接抛异常;- 调用
submitWithFallback(1, 'alipay', 'wechat');- 观察
pay_log表,应只有一条status = success;- 查看日志,应有
并发代付异常记录,但不影响主流程。
5.3 支付结果统一查询:一个接口查所有通道状态,告别“查支付宝用 A 接口,查微信用 B 接口”
用户投诉“钱付没付成功?”时,客服不应登录两个后台。我们抽象出app/Services/PayQueryService.php:
// app/Services/PayQueryService.php public function queryByOrderId($orderId) { $log = DB::table('pay_log') ->where('order_id', $orderId) ->orderByDesc('id') ->first(); if (!$log) { return ['status' => 'not_found', 'msg' => '无代付记录']; } switch ($log->channel) { case 'alipay': $client = new AlipayAopClient(); $result = $client->execute( new \AlipayTradeQueryRequest(), ['out_trade_no' => $log->request_data['out_trade_no']] ); $status = $result->trade_status ?? 'unknown'; break; case 'wechat': $client = new WechatPayClient(); $result = $client->queryOrder(['out_trade_no' => $log->request_data['out_trade_no']]); $status = $result['trade_state'] ?? 'unknown'; break; default: return ['status' => 'unsupported', 'msg' => '不支持的通道']; } return [ 'channel' => $log->channel, 'status' => $status, 'amount' => $log->request_data['total_amount'] ?? $log->request_data['total_fee']/100, 'updated_at' => $log->updated_at ]; }使用场景:
- 客服系统调用
/api/v1/query?order_id=1,返回标准化 JSON;- 前端订单详情页嵌入此接口,实时显示“代付中 / 已成功 / 已关闭”;
- 定时任务扫描
pay_log.status = pending的记录,自动调用此方法补全状态。
从那以后我每次接入新支付通道,都强制走一遍「验签 → 下单 → 查询 → 回调」四步闭环,并用 Postman 保存这四个请求模板。不是为了炫技,而是因为线上每一笔钱,背后都是真金白银的对账责任。少一次验证,就多一分半夜被电话叫醒排查的可能。希望帮到你。
本文还有配套的精品资源,点击获取