thinkphp支付回调service层应接收原始请求体和header,交由sdk验签;验签通过后用事务校验订单并更新状态,配合缓存实现幂等控制,禁止在service中调用$request->param()或响应输出。

ThinkPHP Service层怎么写支付回调处理逻辑
支付回调不能直接在控制器里用 $request->post() 解析,否则微信 XML 或支付宝原始通知结构会被破坏,验签必然失败。必须让 Service 层接收原始请求体,并交由 SDK 自行解析。
- 回调路由要指向一个专用控制器方法(比如
PayNotifyController::handle()),但该方法只做「透传」:把$request->getRawBody()和$request->header()一起交给 Service 处理 - Service 类(如
PayNotifyService)构造时不依赖 Request,只接收原始字符串和头信息,避免容器生命周期污染 - 调用
Pay::wechat($config)->verify($rawBody, $headers)或Pay::alipay($config)->verify($rawBody),SDK 内部会自动识别格式并验签 - 捕获
Yansongda\Pay\Exception\InvalidSignException,记录日志并返回空响应(微信要求 200,支付宝要求空字符串或success)
为什么 PayNotifyService 不能用 $this->request->param()?
因为 $request->param() 会强制解析为数组,把微信的原始 XML 或支付宝的 form-data 全打乱。SDK 验签需要原始字节流 + 正确的 Content-Type 和签名头(比如微信的 Wechatpay-Serial、Authorization)。
客服回复模板。售前咨询、售后处理、退换货、投诉回复、好评引导、升级处理、行业FAQ、满意度挽回。Customer service reply templates for pre-sale, after-sale, returns, complaints, escalation, FAQ generation, s...
- TP6 中间件能拿到未解析的原始体,但 Service 层没中间件上下文,所以必须由上层控制器显式传入
$request->getRawBody() - 支付宝回调是
application/x-www-form-urlencoded,但含 sign 字段,若先被框架 decode 再拼接验签,顺序/编码/空格都会错 - 微信回调是
application/json或application/xml,且验签依赖 headers 中的证书序列号与时间戳,缺一不可
PayNotifyService 怎么安全更新订单状态
验签通过后,不能直接查库改状态——得先查订单是否存在、金额是否匹配、状态是否允许更新,再用事务包裹整个流程。
- 从验签结果中提取
out_trade_no(你生成的订单号)和transaction_id(支付平台流水号) - 用
Db::transaction()包裹:先where('order_sn', $out_trade_no)->lock(true)->find()加行锁,再比对total_amount和数据库中订单金额 - 仅当当前状态为
pending时才更新为paid,并写入pay_no、pay_time - 更新成功后触发事件(如
OrderPaid::dispatch($order)),把发货、发消息等后续动作解耦出去
回调里怎么防止重复通知和重放攻击
微信和支付宝都可能因网络问题多次推送同一笔通知,Service 层必须有幂等控制,不能靠数据库唯一索引硬扛——因为验签失败前就该拦住。
- 验签通过后,立即用
Cache::remember('notify:' . $out_trade_no . ':' . $transaction_id, 3600, fn() => true)记录已处理标记 - 若缓存命中,直接返回成功响应,不走后续逻辑
- 不要用时间戳或随机串做 key:微信通知里没有可靠时间字段,支付宝的
timestamp可被篡改 - 注意缓存失效时间要大于支付平台最大重试窗口(微信是 5 分钟内最多 5 次,支付宝约 2 小时)
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










