支付宝支付回调PHP怎么接收_处理支付宝异步通知参数指南【操作】

必须用 file_get_contents("php://input") 原始读取 POST 数据,因 $_POST 可能为空或截断;验签前需 htmlspecialchars_decode() 解码 fund_bill_list、保留下划线参数名、使用正确 RSA2 公钥;验签通过后立即 echo 'success' 并 exit,业务逻辑异步处理。

怎么正确接收支付宝异步通知的 POST 数据

支付宝异步通知只走 POST,且**不保证参数能被 $_POST 完整捕获**——尤其当含特殊字符(如 fund_bill_list 里的 JSON 字符串)或服务器启用了某些编码过滤时,$_POST 可能为空或被截断。

  • 必须优先用 file_get_contents("php://input") 原始读取请求体,再手动解析
  • 若用框架(如 ThinkPHP5/6),避免直接调 $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_notrade_status)不能被框架自动转成驼峰(如 outTradeNo),验签时字段名必须原样保留
  • 支付宝公钥必须是 RSA2 格式(-----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