扫码支付成功但未查到支付宝通知,如何核对服务器IP?
结论
不建议用固定服务器 IP 判断请求是否来自支付宝,也不应长期将支付宝通知 IP 配置为白名单。支付宝的通知出口可能随网络架构、容灾和调度发生变化,支付订单号也无法用来推导通知服务器 IP。
判断通知来源应以支付宝异步通知的签名校验结果为准,同时调用 alipay.trade.query 主动查询订单状态。如果没有收到通知,还需要检查 notify_url、网关访问日志、防火墙和通知处理程序,确认通知在哪个环节未到达或未被正确记录。
排查步骤
1. 主动查询订单状态
使用商户订单号 out_trade_no 或支付宝交易号 trade_no 调用 alipay.trade.query。
题目中的 2021101022001432285701821660 看起来可能是支付宝交易号,但仍应以创建订单时保存的字段为准,不能只凭号码格式判断。
查询结果需要核对以下内容:
trade_status是否为TRADE_SUCCESS或TRADE_FINISHEDout_trade_no是否属于当前商户total_amount是否与本地订单金额一致app_id、seller_id是否与当前应用和收款账号一致
示例请求逻辑:
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("""
{
"trade_no": "2021101022001432285701821660"
}
""");
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println(response.getTradeStatus());
System.out.println(response.getOutTradeNo());
System.out.println(response.getTotalAmount());
} else {
System.out.println(response.getCode());
System.out.println(response.getSubCode());
System.out.println(response.getSubMsg());
}
实际使用的 SDK 类名和初始化方式,应以项目所用的支付宝 Java SDK 版本为准。
2. 确认下单时传入了正确的 notify_url
异步通知地址通常通过接口请求中的 notify_url 指定。请确认:
- 地址是公网可访问的 HTTPS 地址
- 没有填写
localhost、内网 IP 或测试域名 - 域名能够正常解析,证书有效且证书链完整
- 通知地址不要求登录、验证码或额外鉴权
- 路由允许
POST请求 - 正式环境与沙箱环境没有混用
- 反向代理没有把请求转发到错误的服务
如果下单时没有传入 notify_url、地址填写错误,或者当时无法访问,即使支付成功,业务服务器也不一定能收到异步通知。
3. 检查入口层日志
除了业务应用日志,还应按请求链路依次查看:
- CDN 或云防护日志
- 负载均衡日志
- Nginx、Apache 等 Web 服务器访问日志
- API 网关日志
- 应用程序日志
例如,可以根据通知路径和支付时间检索 Nginx 日志:
grep '/alipay/notify' /var/log/nginx/access.log
检查时重点关注:
- 是否收到了
POST请求 - HTTP 状态码是否为
200 - 是否出现
301、302跳转 - 请求是否被 WAF、防火墙或限流规则拦截
- 请求体是否因大小限制被丢弃
- 应用是否在读取参数前发生异常
如果入口层有请求记录,但应用日志没有,问题通常在反向代理、路由或应用处理逻辑。如果所有入口层都没有记录,则需要继续检查 notify_url 和公网连通性。
4. 使用签名验证通知来源
服务器收到通知后,应使用支付宝公钥验证 sign,不能依赖来源 IP 判断身份。
Java 示例:
@PostMapping("/alipay/notify")
@ResponseBody
public String handleNotify(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((key, values) -> {
if (values != null && values.length > 0) {
params.put(key, String.join(",", values));
}
});
try {
boolean valid = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!valid) {
return "failure";
}
String tradeStatus = params.get("trade_status");
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String totalAmount = params.get("total_amount");
String appId = params.get("app_id");
String sellerId = params.get("seller_id");
// 校验 appId、sellerId、订单金额及订单归属。
// 使用 outTradeNo 做幂等处理,避免重复入账。
// 业务事务成功提交后再返回 success。
return "success";
} catch (AlipayApiException e) {
return "failure";
}
}
这里的公钥必须是与当前应用签名模式匹配的支付宝公钥或支付宝公钥证书,不能错用商户应用公钥。
5. 确保响应内容符合要求
通知处理成功后,应直接返回以下纯文本:
success
不要返回 JSON、HTML,也不要在结果外添加空格或其他包装内容。例如,下面的响应不符合要求:
{"status":"success"}
如果支付宝没有收到符合要求的成功响应,可能会再次发送通知。因此,通知接口必须支持幂等处理,不能把重复通知当作重复付款。
关于服务器 IP 的核对
访问日志中的来源 IP只能辅助排查,不能作为可靠的身份认证依据。需要注意:
open.alipay.com的解析 IP不等于异步通知的出口 IP- 回调可能经过负载均衡、代理或网络调度
- 来源 IP可能发生变化
- 使用 CDN 或反向代理时,日志中的 IP可能是代理节点地址
X-Forwarded-For等请求头可以被伪造,不能替代签名验证
如果企业安全策略强制要求配置 IP 白名单,应通过支付宝开放平台的官方支持渠道确认当前可用的通知网段和变更机制。未经官方确认的单个 IP,不应被写入长期固定规则。
建议的兜底方案
异步通知不应是确认支付结果的唯一方式。订单系统还应具备:
- 异步通知验签
- 通过
alipay.trade.query主动查询 - 对超时订单进行定时补偿查询
- 校验金额、商户身份和订单号
- 根据
out_trade_no或业务订单号执行幂等处理 - 完整保存原始通知参数和验签结果
对于已经显示支付成功但没有收到通知的订单,应先主动查询交易状态,并核对金额和商户身份。确认交易成功后,再通过幂等的补单流程更新本地订单,避免让用户重复付款。
备注:内容仅供参考。
