验签失败主因是框架自动解析导致原始数据被转码,须用file_get_contents('php://input')读原始字符串并urldecode()后parse_str()解析,再传给alipaytradeservice::verifynotify();务必校验rsa公钥格式,并通过notify_id唯一索引实现幂等控制。

验签失败不是支付宝没发,而是你没接住——90% 的问题出在请求体被框架自动解析或转码。
必须用 file_get_contents('php://input') 读原始数据
ThinkPHP 默认会把 POST 数据解码并转成数组,但支付宝签名是基于原始未解码的 query string 计算的。一旦走 $this->request->post() 或 input(),out_trade_no 里的 %2B 可能变成 +,验签直接挂。
- 原始数据是纯字符串,形如
out_trade_no=xxx&trade_status=TRADE_SUCCESS&sign=xxx,不含 JSON、不是 multipart - 必须用
file_get_contents('php://input')一次性读取,不能依赖任何封装过的输入方法 - 读完后立刻
urldecode()一次:支付宝文档说“不编码”,实际字段值(比如商品名)可能含%2B、%2F等 - 再用
parse_str()转成关联数组,喂给 SDK 验签函数
AlipayTradeService::verifyNotify() 的正确调用姿势
别手写排序拼串,SDK 内部已处理字段过滤、字典序排序、签名比对逻辑,但默认读 $_POST,得手动塞原始参数。
- 初始化
AlipayTradeService后,调用$alipay->setRequestParameters($params),其中$params是上一步parse_str()解析出的数组 - 确保
$alipay->alipay_config['rsa_public_key']是支付宝公钥(不是你的私钥),且格式为 PEM,开头必须是-----BEGIN PUBLIC KEY----- - 验签返回
false时,先记日志:file_put_contents('alipay_notify.log', print_r(['raw' => file_get_contents('php://input'), 'server' => $_SERVER['HTTP_USER_AGENT']], true), FILE_APPEND),再返回success,别直接拒单
订单更新必须加幂等控制
支付宝异步通知可能重复推送(网络抖动、你返回非 200、超时重试),同一笔订单来三次,你就发三次货。
- 数据库层:在订单表加
notify_id字段并建唯一索引,每次插入前用INSERT IGNORE或ON DUPLICATE KEY UPDATE - 业务层:状态机判断,只允许从
unpaid→paid,已是paid就跳过发货、库存扣减等操作 - 不要信
trade_status的最终性:它可能滞后,比如先推TRADE_SUCCESS,几秒后再推TRADE_CLOSED;以你自己数据库里落库的状态为准
最常被忽略的是:验签通过后,notify_id 必须存库且去重,否则重试通知照样进业务逻辑。这个字段是支付宝保证通知幂等的唯一凭证,不是可选项。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











