当面付支付成功后未收到异步通知回调
结论
支付日志显示支付成功,并不等于异步通知已经发送到你的回调地址。这个结果可能来自同步响应、轮询查询或主动调用 alipay.trade.query。这些流程与异步通知彼此独立。
当面付成功后没有收到回调,可以重点检查以下问题:
- 发起交易时是否实际传入了
notify_url notify_url是否为支付宝服务器能够直接访问的公网地址- 回调请求是否被网关、HTTPS、鉴权、重定向或防火墙拦截
- 接口返回的成功是否仅表示请求受理成功,而非交易最终成功
- 回调程序是否正确接收表单参数,并返回纯文本
success
不能把异步通知作为唯一的交易确认方式。系统还需要主动查询和补偿处理机制。
先确认日志中的成功指什么
当面付流程中可能出现几种不同的成功状态:
-
调用接口成功
这表示支付宝已经受理请求,不一定代表买家已经付款。 -
客户端或服务端轮询到支付成功
这个状态通常来自alipay.trade.query,只能说明查询时交易处于成功状态。 -
收到支付宝异步通知
支付宝服务器主动请求本次交易设置的notify_url。
所以,即使日志已经记录 TRADE_SUCCESS,也不能据此认定异步通知接口被调用过。
检查发起交易时是否设置了 notify_url
notify_url 必须随交易请求一起提交。只在本地配置文件中填写地址不会自动生效,也不能用页面跳转地址 return_url 代替它。
以 Java SDK 为例:
AlipayClient alipayClient = new DefaultAlipayClient(
gatewayUrl,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
request.setNotifyUrl("https://pay.example.com/alipay/notify");
request.setBizContent("""
{
"out_trade_no": "ORDER_202609130001",
"total_amount": "0.01",
"subject": "测试订单"
}
""");
AlipayTradePrecreateResponse response = alipayClient.execute(request);
需要检查最终发送的请求,确认其中确实包含 notify_url。仅给业务对象或未生效的配置项赋值是不够的。
如果使用第三方封装库,还要确认该库是否真的将回调地址传给了支付宝。部分封装库的全局 notify_url 只对某些方法生效,具体行为取决于所用库及版本。
检查回调地址是否可以从公网访问
支付宝异步通知由支付宝服务器发起,下面这些地址通常无法正常接收通知:
http://localhost/alipay/notify
http://127.0.0.1/alipay/notify
http://192.168.1.10/alipay/notify
回调地址需要满足以下条件:
- 使用可从公网访问的完整 URL
- 域名可以正常解析
- 端口已经向公网开放
- HTTPS 证书有效,证书链完整
- 不要求登录、Cookie、验证码或人工操作
- 不依赖浏览器环境
- 不会被 IP 白名单、WAF、防火墙或反向代理直接拦截
- 尽量避免经过
301、302等重定向
可以通过非本机网络测试接口是否可达,例如:
curl -i -X POST https://pay.example.com/alipay/notify \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "out_trade_no=TEST_ORDER&trade_status=TRADE_SUCCESS"
这个测试只能确认网络和应用路由是否正常,不能代替支付宝签名验证。
确认服务端可以接收 POST 表单
支付宝异步通知通常通过 POST 表单参数发送,并非 JSON 请求。如果接口只接受 application/json,请求可能尚未进入业务代码就返回 400 或 415。
Spring Boot 示例:
@PostMapping(
value = "/alipay/notify",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE
)
public String notify(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((key, values) -> {
params.put(key, String.join(",", values));
});
// 后续进行签名和业务数据校验
return "success";
}
如果框架启用了 CSRF 校验、统一登录拦截器或 API Token 鉴权,需要对支付宝通知路由作相应处理。这里的放行只是允许请求进入回调代码,并不表示可以跳过支付宝签名验证。
正确验签并校验交易信息
收到通知后,不能只根据 trade_status 更新订单。至少需要完成以下检查:
- 使用支付宝公钥验证签名
- 校验
app_id - 校验商户订单号
out_trade_no - 检查支付宝交易号
trade_no是否与现有记录冲突 - 校验金额
total_amount - 校验收款方身份字段
- 确认订单没有被其他交易支付
- 对重复通知进行幂等处理
Java 验签示例:
boolean signVerified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!signVerified) {
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");
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 校验 app_id、订单号、金额和收款方信息
// 使用数据库事务和唯一约束保证幂等
// 更新订单状态
}
return "success";
参与验签的实际参数应遵循 SDK 的要求。不要自行修改、排序、转码或拼接参数后再验签。
必须按要求返回 success
业务处理完成后,响应体应直接返回以下纯文本:
success
不要返回 JSON、HTML 或附带额外说明的内容,例如:
{"code": 200, "message": "success"}
下面的响应虽然含有单词 success,仍可能不符合通知确认要求:
success received
还要保证 HTTP 状态码正常,并避免回调处理耗时过长。建议按以下顺序处理:
- 验签并校验关键字段。
- 快速、幂等地落库或写入可靠队列。
- 返回纯文本
success。 - 异步执行发货、积分和短信等耗时业务。
如果处理失败,应记录完整错误信息,并按照实际协议返回失败结果,以便通知重试机制再次投递。
检查服务器日志链路
业务应用中完全没有请求日志,不一定说明支付宝没有发起请求。可以按顺序检查:
- CDN 或云负载均衡访问日志
- WAF、安全组和防火墙日志
- Nginx、Apache 或网关访问日志
- 应用服务器访问日志
- 回调控制器入口日志
- 验签和订单处理日志
在 Nginx 日志中可以重点查找:
POST /alipay/notify
常见异常状态码包括:
301、302:请求发生了重定向400:请求参数或协议不符合应用要求401、403:请求被鉴权、CSRF、WAF 或访问控制拒绝404:路由配置错误405:接口不允许 POST415:接口不支持表单 Content-Type500、502、504:应用或上游服务发生异常
记录通知参数时需要脱敏,不要把私钥、完整签名材料或其他敏感信息写入日志。
不要只依赖异步通知
网络请求可能出现延迟、失败或重复,订单系统必须具备主动查询补偿机制。
以下场景可以调用 alipay.trade.query:
- 用户支付后,前端长时间停留在等待页面
- 订单超过一定时间仍处于
支付中 - 收银端已经显示成功,但服务端没有收到异步通知
- 对账时发现支付宝状态与本地状态不一致
伪代码示例:
if (order.isPaying() && order.hasTimedOut()) {
TradeQueryResult result = queryAlipayTrade(order.getOutTradeNo());
if ("TRADE_SUCCESS".equals(result.getTradeStatus())
|| "TRADE_FINISHED".equals(result.getTradeStatus())) {
confirmPaymentIdempotently(
order,
result.getTradeNo(),
result.getTotalAmount()
);
}
}
异步通知和主动查询最终应调用同一个幂等的确认支付方法,防止重复记账、重复发货或重复增加余额。
推荐排查顺序
- 抓取或记录发起交易时的实际请求,确认其中包含正确的
notify_url。 - 确认查询结果确实为
TRADE_SUCCESS或相应的最终状态。 - 从公网测试回调 URL 的 DNS、HTTPS、端口和 POST 路由。
- 检查网关、Nginx、WAF 和应用访问日志。
- 排除重定向、登录鉴权、CSRF 和 Content-Type 限制。
- 检查验签过程,并确认回调响应体为纯文本
success。 - 使用新的商户订单号重新测试,避免旧订单状态影响判断。
- 增加
alipay.trade.query定时补偿和对账机制。
如果发起请求已经明确携带有效的公网 notify_url,交易也已进入最终成功状态,但服务器所有入口日志都没有回调记录,应保留 app_id、out_trade_no、trade_no、接口响应和请求时间等信息,再通过对应支付平台的技术支持渠道核查通知投递记录。不要在工单或公开日志中提交应用私钥。
备注:内容仅供参考。
