支付宝沙箱扫码成功后未立即回调,需要设置什么?
结论
这是正常现象。支付宝扫码支付通常不会在用户扫码后立即向商户服务器发送异步通知。只有交易状态发生变化,例如支付成功后,支付宝才会通过 notify_url 发送通知。
因此,无需配置所谓的“扫码成功回调”参数。如果业务需要了解支付进度,应主动调用 alipay.trade.query 查询交易状态,不能依赖扫码回调。
为什么扫码后没有回调
以当面付预下单接口 alipay.trade.precreate 为例,生成二维码、用户扫码和完成支付是三个不同阶段:
- 商户调用
alipay.trade.precreate生成二维码。 - 用户使用支付宝扫描二维码。
- 用户确认并完成支付。
- 支付宝向商户配置的
notify_url发送支付结果通知。
二维码被扫描通常不属于异步通知事件。支付宝的异步通知主要用于告知商户交易结果,例如支付成功后的 TRADE_SUCCESS 或 TRADE_FINISHED。
如果用户扫码后没有继续付款,商户可能一直收不到异步通知。这不代表回调配置有误。
需要配置什么
创建交易时,需要正确设置异步通知地址 notify_url。不同语言和 SDK 版本的写法可能不同,但都需要将通知地址作为请求的公共参数提交。
Java SDK 的常见写法如下:
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
request.setNotifyUrl("https://example.com/api/alipay/notify");
request.setBizContent("""
{
"out_trade_no": "ORDER_202609090001",
"total_amount": "0.01",
"subject": "测试订单"
}
""");
AlipayTradePrecreateResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println(response.getQrCode());
} else {
System.out.println(response.getSubCode());
System.out.println(response.getSubMsg());
}
具体方法签名要以项目实际使用的支付宝 SDK 版本为准。部分 SDK 也支持在执行请求时传入通知地址。
notify_url 需要满足以下条件:
- 支付宝服务器可以通过公网访问;
- 建议使用证书有效的 HTTPS 地址;
- 不能填写
localhost、127.0.0.1或只能从内网访问的地址; - 接口不能依赖浏览器登录状态;
- 网关、防火墙和反向代理需要允许支付宝的请求到达;
- 请求处理完成后,需要返回纯文本
success。
支付结果回调处理示例
下面是一个简化的 Java Servlet 示例:
protected void doPost(
HttpServletRequest request,
HttpServletResponse response
) throws IOException {
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
params.put(entry.getKey(), String.join(",", entry.getValue()));
}
boolean signVerified;
try {
signVerified = AlipaySignature.rsaCheckV1(
params,
ALIPAY_PUBLIC_KEY,
"UTF-8",
"RSA2"
);
} catch (AlipayApiException e) {
response.getWriter().print("failure");
return;
}
if (!signVerified) {
response.getWriter().print("failure");
return;
}
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
String appId = params.get("app_id");
// 还应校验 app_id、商户订单号、订单金额和收款方信息。
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 使用事务或唯一约束更新订单,确保重复通知不会重复入账。
markOrderPaid(outTradeNo, tradeNo, totalAmount, appId);
response.getWriter().print("success");
return;
}
response.getWriter().print("success");
}
处理回调时,不能只根据 trade_status 更新订单。至少还要核对以下信息:
app_id是否属于当前应用;out_trade_no是否存在;total_amount是否与本地订单金额一致;seller_id或其他收款方信息是否符合预期;- 签名是否验证通过。
如何获取扫码后的交易进度
如果收银台需要及时显示支付状态,后端可以定时调用 alipay.trade.query:
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("""
{
"out_trade_no": "ORDER_202609090001"
}
""");
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
String tradeStatus = response.getTradeStatus();
switch (tradeStatus) {
case "WAIT_BUYER_PAY":
// 等待用户付款
break;
case "TRADE_SUCCESS":
case "TRADE_FINISHED":
// 支付成功
break;
case "TRADE_CLOSED":
// 交易关闭
break;
default:
// 记录未知状态,避免直接按支付成功处理
break;
}
}
前端可以每隔几秒请求一次自己的后端,再由后端查询支付宝,或者读取已经更新的本地订单状态。轮询时应设置合理的查询间隔和超时时间,并在订单超时后停止查询。
WAIT_BUYER_PAY 只能说明交易尚未完成付款,不能将其视为“已经扫码”。能否准确识别扫码动作,要看具体产品和接口是否提供相应能力。普通预下单支付流程不应假设存在扫码成功通知。
排查支付成功后仍收不到通知
如果用户完成支付后仍未收到异步通知,可以按下面的顺序检查:
- 创建交易时是否确实传入了
notify_url。 - 通知地址能否从公网直接访问。
- HTTPS 证书、域名解析和端口是否正常。
- 接口是否被登录验证、CSRF、防火墙或网关规则拦截。
- 服务端是否正确接收
POST表单参数。 - 验签时是否使用支付宝公钥,而不是应用公钥。
- 回调处理成功后是否返回了纯文本
success。 - 是否误将前台跳转地址
return_url当成异步通知地址。
return_url 用于浏览器页面跳转。用户关闭页面或网络中断都可能影响跳转结果,因此不能将它作为订单支付成功的最终依据。订单状态应以验签通过的异步通知为主,并使用 alipay.trade.query 主动查询作为补偿。
在沙箱中,扫码后没有立即回调,付款完成后才收到通知,通常符合支付流程。需要配置的是接收支付结果的 notify_url,不是扫码成功回调。
备注:内容仅供参考。
