vivo支付回调$_POST为空是因为使用application/json格式,需用file_get_contents('php://input')读取并json_decode;验签需URL解码后字典序拼接参数+app_secret再hmac-sha256;必须HTTPS、5秒内返回纯文本success;防重靠order_no与trade_no组合幂等。

收到 vivo 支付回调时,$_POST 为空?检查请求体和编码
vivo 的支付通知回调(notify_url)默认用 application/json 发送,不是表单提交。直接读 $_POST 肯定为空——这是最常踩的坑。
- 必须用
file_get_contents('php://input')读原始请求体 - 返回的是 JSON 字符串,需
json_decode($raw, true)解析成数组 - vivo 文档里明确写了“通知为 POST 请求,Content-Type: application/json”,但很多人跳过这句
- 如果用 Nginx,确认没开启
client_body_buffer_size过小或client_max_body_size限制,否则可能截断 JSON
验签失败:vivo 的 sign 是对排序后参数拼接再 SHA256 + 密钥
不是简单对整个 JSON 签名,也不是对未排序字段签名。vivo 要求先剔除 sign 字段,把剩余字段按字典序升序排列,拼成 key1=value1&key2=value2 形式,再拼上你在开放平台配置的 app_secret,最后做 hash_hmac('sha256', $str, $app_secret)。
- 注意:value 必须 URL 解码后再参与拼接(vivo 会把中文等编码成 %xx)
- 空值字段(
""或null)要保留,不能跳过 - 常见错误:漏掉
app_secret、用错哈希算法(比如用了 md5)、没做 URL 解码导致验签不通过 - 建议把拼接逻辑单独抽成函数,加日志输出拼接前的字符串,方便比对
回调地址被 vivo 拒绝:检查 HTTPS、响应状态码和超时
vivo 要求回调地址必须是有效 HTTPS,且服务器必须在 5 秒内返回 HTTP 200(且响应体为纯字符串 success),多一个空格、换行或 JSON 都算失败。
- 响应头不能含
Content-Type: application/json,只允许文本;用header('Content-Type: text/plain; charset=utf-8'); - PHP 脚本开头不要有
echo、BOM、空白字符,避免输出干扰 - 别在验签通过前就写日志到慢速磁盘,或调用外部 API,容易超时
- 测试时用
curl -v -X POST https://your.com/vivo-notify -H "Content-Type: application/json" -d '{"..."}'模拟,看是否真返回 200 + success
重复通知怎么防重?靠 order_no + 本地幂等记录
vivo 明确说明“通知可能多次发送”,不会带唯一请求 ID,只能靠业务字段。order_no 是订单号,trade_no 是 vivo 侧交易号,两者组合可唯一标识一笔支付成功事件。
立即学习“PHP免费学习笔记(深入)”;
- 收到回调后,先查本地数据库是否存在该
order_no且状态为“已支付” - 存在则直接返回
success,不处理;不存在则走完整流程(更新状态、发奖、记录vivo_trade_no) - 别依赖
out_trade_no单独判断——它只是你传过去的,可能重复;必须结合 vivo 返回的trade_no做二次校验 - 建议在事务里完成状态更新和记录插入,避免中间态导致重复发放











