支付宝沙箱支付成功后提示网络繁忙,是环境还是代码问题?
支付老李
结论
异步回调能够正常到达并通过验签,一般说明支付交易已经完成,支付接口、商户密钥和 notify_url 的基本配置也没有问题。
支付成功后,页面仍提示“网络繁忙”,问题通常出在同步跳转链路,例如支付宝沙箱页面临时异常、return_url 无法访问、同步回跳处理报错,或者浏览器网络异常。单凭页面提示无法确定是否为沙箱故障,还要结合访问日志,确认请求停在哪个环节。
为什么回调成功,页面仍会报错
支付宝支付完成后,通常会经过两条彼此独立的链路:
- 异步通知:支付宝服务器请求商户的
notify_url,用于可靠地通知支付结果。 - 同步跳转:用户浏览器跳转到
return_url,主要用于展示支付结果。
异步通知成功,不等于同步跳转也一定成功。即使同步页面提示“网络繁忙”,订单也可能已经支付完成。
业务系统应以异步通知或主动查询得到的交易状态为准,不能只根据浏览器的同步跳转结果更新订单状态。
如何判断是沙箱环境还是系统代码
1. 检查 return_url 是否收到请求
查看网关、Web 服务器和应用日志,搜索支付完成时间附近的 return_url 访问记录。
如果完全没有请求到达:
- 问题可能出在支付宝沙箱页面或浏览器跳转阶段。
- 检查
return_url是否填写正确。 - 确认使用的是完整 URL,并且可以从外网访问。
- 检查域名解析、HTTPS 证书、端口和防火墙配置。
- 换用无痕窗口或其他浏览器重新测试,排除缓存和插件的影响。
- 稍后再试。如果同一套代码此前运行正常,配置也没有变化,沙箱环境短暂不稳定的可能性较高,但还不能据此直接认定是支付宝故障。
如果请求已经到达:
- 查看接口是否返回
4xx、5xx,或者发生超时。 - 检查同步验签、参数解析、数据库查询和页面渲染过程中是否抛出异常。
- 确认处理方法没有覆盖或丢弃支付宝传入的参数。
2. 确认提示出现在哪个页面
检查浏览器地址栏和开发者工具中的 Network 记录:
- 如果页面仍停留在支付宝沙箱域名,并且请求没有进入商户系统,更可能是沙箱或跳转链路异常。
- 如果页面已经进入自己的域名,应优先检查
return_url对应的代码。 - 如果自己的接口又重定向到支付宝或其他地址,需要继续检查整个重定向链路,防止出现循环跳转。
3. 核对同步与异步配置
重点检查以下配置:
return_url和notify_url是否写反。- 近期是否修改过域名、网关路径、反向代理或 HTTPS 配置。
- 沙箱应用是否使用了沙箱网关、沙箱账号和对应的支付宝公钥。
charset、sign_type等配置是否与请求和验签代码一致。- 同步请求所用的应用和公钥配置,是否与发起支付时使用的是同一套环境。
生产环境公钥、沙箱公钥和应用私钥不能混用。
同步回跳处理示例
下面以 Java SDK 为例。具体类名和依赖版本应以项目当前使用的支付宝 SDK 为准,排查期间不要临时更换 SDK 版本。
@GetMapping("/alipay/return")
public String alipayReturn(HttpServletRequest request, Model model) {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((name, values) -> {
params.put(name, String.join(",", values));
});
try {
boolean verified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!verified) {
model.addAttribute("message", "支付结果验签失败");
return "pay-result";
}
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
// 这里只展示结果,不应仅凭同步回跳将订单更新为已支付。
model.addAttribute("outTradeNo", outTradeNo);
model.addAttribute("tradeNo", tradeNo);
model.addAttribute("message", "支付结果正在确认");
return "pay-result";
} catch (Exception e) {
// 日志中不要记录私钥、完整敏感参数或用户隐私数据。
log.error("支付宝同步回跳处理失败", e);
model.addAttribute("message", "支付结果查询失败,请稍后查看订单状态");
return "pay-result";
}
}
同步页面可以根据商户订单号查询本地订单状态。如果异步通知还没有处理完成,可以显示“支付结果确认中”,也可以由后端调用支付宝交易查询接口复核,不要直接提示支付失败。
异步通知处理要点
处理异步通知时,必须先验签,再核对订单信息和交易状态:
@PostMapping("/alipay/notify")
@ResponseBody
public String alipayNotify(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((name, values) -> {
params.put(name, String.join(",", values));
});
try {
boolean verified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!verified) {
return "failure";
}
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String tradeStatus = params.get("trade_status");
// 还应核对 app_id、金额、收款方等关键字段。
// 更新订单时必须保证幂等,防止重复通知造成重复入账。
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
paymentService.confirmPayment(outTradeNo, tradeNo);
}
return "success";
} catch (Exception e) {
log.error("支付宝异步通知处理失败", e);
return "failure";
}
}
返回给支付宝的通知响应必须是接口要求的纯文本结果,不能夹带 HTML、JSON、空格或调试信息。
推荐的排查顺序
- 根据异步通知中的
out_trade_no和trade_no,确认订单是否确实支付成功。 - 检查支付完成时,服务器访问日志中是否出现了对
return_url的请求。 - 如果请求已经进入服务器,检查同步接口异常、验签失败、超时和页面渲染错误。
- 如果请求没有进入服务器,检查浏览器 Network 记录、回跳地址和公网可访问性。
- 使用相同配置重新创建一笔沙箱订单,避免继续使用状态异常的测试订单。
- 如果代码和配置都没有变化,
return_url可以正常直接访问,但请求始终没有到达,可以隔一段时间复测。同时保留发生时间、请求参数、订单号和页面截图,以便向支付宝技术支持反馈。
注意事项
- 同步页面能否正常显示,不能作为判断支付成功的最终依据。
- 不要因为同步跳转失败,就把已经支付的订单改为失败或关闭。
- 支付宝可能重复发送异步通知,因此订单更新必须具备幂等性。
- 即使验签成功,也要继续核对订单号、金额、应用身份和收款方等字段。
- 沙箱环境可能出现临时波动,但只有日志能够证明请求没有进入自己的系统时,才能较有把握地判断是环境问题。
- 如果前几天还能正常使用,也要检查近期是否有代码发布、域名调整、证书更新、代理规则修改或沙箱密钥重新配置。
备注:内容仅供参考。
