做支付对接这几年,被问得最多的故障就是支付回调收不到。用户那边付款成功的页面都弹出来了,你网站里的订单还挂着一个待支付,钱到没到不知道,用户急,你更急。这类问题看起来玄乎,其实大部分都能在十分钟内找到原因,前提是排查顺序要对。
彩虹易支付这类聚合支付平台的异步通知,机制都是同一套:用户付完款,平台服务器主动往你配置的 notify_url 发一个请求,把支付结果告诉你。从用户付款到你的订单状态更新,中间隔着一整条链路,任何一个环节出问题,支付回调就断了。这篇文章把我平时帮站长排查的顺序整理出来,按五步走,基本能覆盖九成故障。
先分清三种故障现象,别一上来就瞎查
同样是支付回调收不到,根因可能完全不一样。动手之前先确认自己属于下面哪一类,排查方向直接少一半。
现象一:平台完全没发起回调
服务器日志里搜不到任何 notify 相关的记录。这说明请求压根没打进来,问题大概率出在 notify_url 本身或者网络链路上,跟程序代码关系不大。
现象二:回调进来了,程序处理报错
日志里能看到请求,但返回的是 404 或者 500。请求到了,是你的程序没接住,要么 notify_url 路径不对,要么代码报错。这类故障跟网络无关,纯粹是程序问题。
现象三:回调重复推送,订单状态反复变
这种严格来说不算收不到,是收到了没处理干净。用彩虹易支付的时候也常见:平台没收到正确的 success,判定失败后会对同一笔异步通知反复重推,你的订单在待支付和已支付之间来回跳。
五步排查流程
按下面的顺序查,多数故障在前两步就能现形。
第一步:确认 notify_url 公网能访问
这个坑新手踩得最多。本地开发时用 127.0.0.1 或者内网 IP 测试,跑通了就上线,忘了把 notify_url 改成公网域名。支付平台的服务器是主动来请求你的 notify_url 的,它访问不到你电脑的内网地址。最快的验证办法:关掉 WiFi,用手机流量直接访问一次你的回调地址,通了说明公网可达;不通就去查域名解析和部署。彩虹易支付商户后台里配置的异步通知地址,必须是 https 开头的公网地址。
第二步:检查服务器防火墙和云安全组
云服务器的安全组、宝塔面板的防火墙,哪一层都能把请求挡在门外。我遇到过不止一个案例:站点一切正常,支付回调就是不来,最后发现安全组只放行了 80 端口,443 没放行,HTTP 正常、HTTPS 的异步通知全部失败。支付平台发回调默认走 HTTPS,443 端口必须对外放行。
第三步:翻 Nginx 日志定位
日志路径一般在 /www/wwwlogs/你的域名.log,宝塔环境都在这。直接搜 notify_url 关键词,配合 notify 一起看:有请求进来但返回 404,说明回调地址路径写错了;返回 500,说明你的回调程序抛异常了,去翻 PHP 或 Python 的错误日志。这一步能把链路问题和程序问题一刀切开。
第四步:核对签名校验
回调参数里带着一个 sign 字段,是支付平台用商户密钥算出来的。你这边要按同样的规则重算一遍:参数按 ASCII 排序、拼接商户密钥、做 MD5,然后和传过来的 sign 对比。签名校验对不上只有两种可能,密钥配置错了,或者参数在传输中丢字段了。签名规则在彩虹易支付的 API 文档里写得清楚,【此处内链指向彩虹易支付API签名文档页面】。我处理过的签名校验失败里,一大半是复制密钥时多带了空格。
第五步:确认返回格式是纯文本 success
回调处理完,必须原样输出纯文本 success,JSON 不行,code 字段包装也不行。平台只认这一个词,收到别的就判定失败,过几分钟按你的 notify_url 重推,再失败再推。很多人以为支付回调丢了,其实是回调来了但被你拒了。还有更隐蔽的:success 前后不能带空格、换行、注释,有些框架模板会自动追加空行,这就是排查半天没头绪的原因。
几个冷门坑,补上才完整
CDN 和 WAF 可能拦截支付服务器的 IP
站点套了 CDN 或者 WAF 防护的,注意支付平台的服务器 IP 可能被规则误伤。CDN 回源不稳、WAF 把平台请求当成攻击流量拦掉,支付回调就会时好时坏。稳妥的做法是把平台回调 IP 加白名单,或者让异步通知直连源站。
SSL 证书过期或链路不兼容
证书过期、证书链不完整、或者平台请求客户端不兼容你的证书版本,HTTPS 握手失败,彩虹易支付的支付回调就进不来。这类问题浏览器访问时根本发现不了,只有服务器对服务器的请求才会暴露。
回调程序执行超时
回调入口里如果塞了太多同步操作,比如回调进来又去请求一次订单查询接口,执行时间拉长,平台的请求超时断开,等于没处理完。支付回调处理要快,耗时的活放队列里,异步通知本来就该轻量。
两个落地技巧:参数日志和防重复下单
排查传参问题,最快的办法是在回调入口把完整参数写进日志文件,POST 参数和请求体原样记录,顺带把签名校验结果也打出来,扫一眼就知道少了哪个字段、sign 对不对。防重复下单,给订单号加唯一索引,回调进来先查订单状态,已经处理过的直接返回 success,避免平台重复推送异步通知造成重复发货。
总结
支付回调收不到的排查主线就一条:先分清楚是请求没来、来了报错、还是重复推送,然后从 notify_url 公网可达开始,依次查 443 端口、Nginx 日志、签名校验、success 返回格式。多数故障在前两步就现形了,后三步解决的是到了但处理错的问题。把这五步过一遍,再留个心眼防着 CDN、证书、超时这几个冷门坑,回调类故障基本都能自己搞定。