必须用 file_get_contents("php://input") 原始读取 POST 数据,因 $_POST 可能为空或截断;验签前需 htmlspecialchars_decode() 解码 fund_bill_list、保留下划线参数名、使用正确 RSA2 公钥;验签通过后立即 echo 'success' 并 exit,业务逻辑异步处理。
支付宝异步通知只走 POST,且**不保证参数能被 $_POST 完整捕获**——尤其当含特殊字符
(如 fund_bill_list 里的 JSON 字符串)或服务器启用了某些编码过滤时,$_POST 可能为空或被截断。
file_get_contents("php://input") 原始读取请求体,再手动解析$this->request->post(),应改用 $this->request->getRawInput() 或等效方法file_put_contents('notify.log', print_r($raw, true), FILE_APPEND) 记日志,别等出问题再翻验签失败不是“公钥填错了”这么简单,多数卡在参数预处理环节。支付宝要求验签前必须还原原始语义,否则 rsaCheckV1() 必然返回 false。
fund_bill_list 是 HTML 实体编码过的 JSON 字符串,必须先用 htmlspecialchars_decode() 解码,否则验签字段不匹配out_trade_no、trade_status)不能被框架自动转成驼峰(如 outTradeNo),验签时字段名必须原样保留-----BEGIN PUBLIC KEY----- 开头),且不能混用证书模式下的公钥文件(如 alipayCertPublicKey_RSA2.crt 内容需提取 PEM 段)支付宝会在 24 小时内最多重试 10 次异步通知,且只要没收到纯文本 success 就持续发。如果你在验签后立刻执行耗时操作(比如查库存、调物流 API、发短信),响应超时 → 支付宝认为失败 → 再次推送 → 订单重复处理。
echo 'success'; exit;**,其他逻辑全部扔进队列或异步进程file_put_contents() 或 Redis 存原始通知数据,另起一个定时任务/消费者处理业务UNIQUE KEY(out_trade_no, trade_status))或先 SELECT ... FOR UPDATE 锁行别等真支付才测,本地就能模拟。关键点:HTTP 头、签名字段、原始 POST Body 三者必须一致。
curl 发送时,必须指定 -H "Content-Type: application/x-www-form-urlencoded",不能用 application/json
sign 要用你自己的私钥 + 其他所有参数(不含 sign 自身)按字典序拼接后生成,官方 SDK 的 rsaSign() 才可靠file_get_contents("php://input") 接收,硬编码进你的 notify.php,跑通再换真实逻辑curl -X POST http://yourdomain.com/notify.php \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "out_trade_no=202512291158000001" \ --data-urlencode "trade_status=TRADE_SUCCESS" \ --data-urlencode "total_amount=1.00" \ --data-urlencode "sign=xxx..."
真正难的从来不是写完代码,而是确认每一次回调都只被处理一次、且每次都能在 1 秒内干净利落地回 success。