thinkphp6支付回调处理核心是验签前置、逻辑解耦、幂等保障与对账兜底:控制器仅验签并投递队列,业务逻辑在队列任务中用行锁/redis锁实现幂等,定时对账补单。

ThinkPHP6 的支付回调处理,核心是确保验签、幂等、业务逻辑解耦和安全可靠。不能直接在回调里写订单更新、发货等重逻辑,更不能跳转页面或输出非字符串内容。
验签必须前置且严格
所有主流支付(微信、支付宝)回调都要求先验签,失败立即返回失败响应,不执行后续逻辑。TP6 中建议封装统一验签方法:
- 从 $request->param() 或 $request->getRawBody()(微信 XML)获取原始数据
- 调用官方 SDK 的验签方法(如 AlipayAopClient::rsaCheckV1() 或 Pay::wechat()->verify()),传入公钥/平台证书
- 验签失败时,返回对应协议要求的响应(微信返回 success 或空字符串,支付宝返回 success),并记录日志
回调入口要轻量、无状态、只做分发
控制器方法应仅完成“接收→验签→写日志→投递队列”,避免数据库操作、模板渲染、重定向:
- 不要使用 redirect()、view()、echo/json 等输出;微信/支付宝只认 HTTP 200 + 特定响应体
- 推荐用 TP6 内置队列(如 Redis 驱动)把支付结果推入任务,例如:PayCallbackJob::dispatch($data)
- 控制器内记录原始请求、IP、时间、验签结果,便于排查“重复通知”或“伪造回调”
业务逻辑全部下沉到队列任务中
队列任务类负责真正处理:查订单、更新状态、发消息、扣库存、触发通知等。这样既防并发冲突,也避免超时被支付方重试:
- 根据回调中的 out_trade_no 查询本地订单,确认是否存在且未处理
- 用数据库行锁(forUpdate())或 Redis 分布式锁保障幂等,防止同一笔订单被多次处理
- 成功更新后记录支付流水(trade_no、pay_time、callback_raw 等),失败则重试(设置最大重试次数)或告警
主动查询 + 对账作为兜底手段
依赖回调不可靠(网络丢包、服务宕机、支付方延迟推送),必须搭配定时对账:
- 每日凌晨调用支付平台的“交易查询接口”,拉取当天 TRADE_SUCCESS 但本地无记录的订单
- 比对金额、时间、状态,补单并记录对账日志
- 对账脚本可封装为命令行指令,通过 php think pay:reconcile 手动或 cron 自动执行
不复杂但容易忽略:回调地址必须是公网可访问、HTTPS、无重定向、不带 query 参数(微信尤其敏感),且 Nginx/Apache 要允许 POST 大请求体(如微信 XML 可能超 1MB)。











