PAYATHON 2026

支付宝沙箱支付出现504 Gateway Time-out等错误

支付阿杰

结论

只配置 appid、appPrivateKey、alipayPublicKey 和 notifyUrl,并不能保证沙箱支付成功。

遇到 504 Gateway Time-out 或“系统有点忙,一会儿再试试”,一般从两方面排查:

  1. 支付宝沙箱网关暂时异常、响应超时或正在维护。如果请求已经进入支付宝页面,而且错误由支付宝返回,更可能是这类问题。
  2. 请求环境与签名配置不一致。常见情况包括网关地址错误、密钥不匹配、sign_type 不一致、请求参数不合法,以及误把应用公钥填成支付宝公钥。

可以先用最小支付请求验证签名和网关,再检查网络、业务参数与异步通知地址。不要看到页面提示“系统繁忙”就认定代码有问题,也不要反复更换密钥碰运气。

先确认沙箱网关

沙箱环境应请求:

https://openapi-sandbox.dl.alipaydev.com/gateway.do

正式环境通常使用:

https://openapi.alipay.com/gateway.do

沙箱 appid、沙箱账号和沙箱密钥只能用于沙箱环境。把正式网关和沙箱配置混在一起,通常会引发验签、应用状态或权限错误。

使用支付宝 SDK 时,需要明确设置沙箱网关:

AlipayClient alipayClient = new DefaultAlipayClient(
    "https://openapi-sandbox.dl.alipaydev.com/gateway.do",
    appId,
    appPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

不同版本 SDK 的构造方法可能有所不同,具体应以当前版本的官方 SDK 接口为准。无论使用哪个版本,网关、字符集和签名算法都要保持一致。

检查公钥和私钥是否配对

配置时最容易混淆的是几种公钥的用途:

  • appPrivateKey 是应用私钥,由商户自行保存,用来给请求签名。
  • 应用公钥与应用私钥配对,需要上传到支付宝开放平台。
  • alipayPublicKey 是支付宝公钥,由开放平台提供,用来验证支付宝响应和通知的签名。

SDK 的 alipayPublicKey 参数必须填写支付宝公钥,不能填写应用公钥。

还需要逐项确认:

  • 密钥属于当前沙箱 appid,不是其他应用的密钥。
  • 开放平台中的应用公钥与本地 appPrivateKey 配对。
  • 平台配置为 RSA2 时,代码中的 sign_type 也使用 RSA2。
  • 私钥没有缺行,也没有因空格或复制操作而损坏、截断。
  • SDK 需要的是原始 Base64 密钥还是 PEM 格式,应按对应语言 SDK 的要求传入。

PEM 私钥通常类似:

-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----

如果 SDK 只接受去掉头尾的 Base64 内容,就不能直接传入完整的 PEM 文本。反过来也一样。

使用最小参数发起支付

排查时先删去可选参数,只保留必要字段。以电脑网站支付为例:

AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();

request.setReturnUrl("https://example.com/pay/return");
request.setNotifyUrl("https://example.com/pay/notify");

request.setBizContent("""
{
  "out_trade_no": "TEST202609090001",
  "total_amount": "0.01",
  "subject": "沙箱支付测试",
  "product_code": "FAST_INSTANT_TRADE_PAY"
}
""");

AlipayTradePagePayResponse response = alipayClient.pageExecute(request);

if (response.isSuccess()) {
    String form = response.getBody();
    System.out.println(form);
} else {
    System.err.println("code=" + response.getCode());
    System.err.println("subCode=" + response.getSubCode());
    System.err.println("msg=" + response.getMsg());
    System.err.println("subMsg=" + response.getSubMsg());
    System.err.println("body=" + response.getBody());
}

out_trade_no 在商户侧必须唯一。重复使用已经支付或状态异常的订单号,可能引发重复订单或交易状态错误。每次排查最好生成一个新订单号。

如果调用的是手机网站支付、App 支付或当面付,需要改用对应的 API 和正确的 product_code,不能直接照搬电脑网站支付的参数。

确认 504 出现在哪一层

504 Gateway Time-out 不一定来自支付宝,首先要确认响应是由谁返回的。

请求尚未到达支付宝

如果 504 页面带有 Nginx、网关、CDN 或本公司域名等特征,问题可能出在自己的反向代理或后端服务,例如:

  • 应用连接支付宝接口时超时。
  • DNS、代理、防火墙或出口网络异常。
  • Nginx、Ingress 或 API Gateway 的读取超时设置过短。
  • 后端线程阻塞,没有及时向浏览器返回支付表单或跳转地址。

排查时可以记录:

请求时间:
请求网关:
HTTP 状态码:
响应头:
响应体:
支付宝接口 code:
支付宝接口 sub_code:
支付宝接口 msg:
支付宝接口 sub_msg:

不要只记一条“支付失败”。定位参数、权限和签名问题时,code 与 sub_code 更有用。

已经跳转到支付宝页面

如果浏览器已经进入支付宝沙箱域名,之后才显示“系统有点忙”或 504,请求大概率已经到达支付宝。可以依次尝试:

  1. 换一个新的 out_trade_no。
  2. 退出沙箱买家账号,然后重新登录。
  3. 清除浏览器 Cookie,或者使用无痕窗口。
  4. 等一段时间再试,避免短时间内连续提交。
  5. 更换网络或直接请求沙箱网关,排除本地代理的影响。
  6. 查询订单状态,确认支付宝是否已经创建交易。
  7. 保存请求时间、appid、接口名称及支付宝返回内容,必要时交给支付宝技术支持排查。

沙箱环境偶尔会有服务不稳定的情况。不过,只有排除签名、参数、网关和网络问题后,才能较有把握地判断故障来自沙箱服务。

验证网络连通性

应从实际部署支付服务的机器测试沙箱网关,不要只在个人电脑上测试:

curl -I --connect-timeout 10 --max-time 30 \
  "https://openapi-sandbox.dl.alipaydev.com/gateway.do"

这个命令只能检查 DNS、TLS 和基础 HTTP 连通性,不能证明业务请求一定能够成功。即使返回 4xx,只要服务器及时响应,通常也说明基础网络是通的。如果连接持续超时,应检查:

  • 服务器 DNS。
  • HTTPS 出站访问策略。
  • 防火墙或安全组。
  • HTTP/HTTPS 代理配置。
  • TLS 证书链和系统时间。
  • 容器或云主机的出口网络。

notifyUrl 不是支付页面的跳转地址

notifyUrl 用于接收支付宝服务器发送的异步通知,不负责将用户浏览器跳回商户网站。浏览器在支付完成后的跳转地址通常是 returnUrl。

合格的 notifyUrl 需要满足以下条件:

  • 支付宝服务器可以通过公网访问。
  • 不使用 localhost、127.0.0.1 或内网 IP。
  • 尽量使用有效的 HTTPS 证书。
  • 不依赖浏览器 Cookie 或登录状态。
  • 能够接收支付宝实际使用的请求方法和表单参数。
  • 验签成功并完成业务处理后,返回纯文本 success。
  • 同一条通知重复到达时,不会重复入账。

notifyUrl 无法访问,通常会触发支付宝重试异步通知,而不是直接导致支付页面出现 504。因此,不能只围绕通知地址排查。

通知处理的核心逻辑可以写成:

Map<String, String> params = extractAlipayParameters(request);

boolean verified = AlipaySignature.rsaCheckV1(
    params,
    alipayPublicKey,
    "UTF-8",
    "RSA2"
);

if (!verified) {
    response.getWriter().write("failure");
    return;
}

String appIdFromNotify = params.get("app_id");
String outTradeNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");

// 还应核对 app_id、订单号、订单金额和收款方信息。
// 更新订单时必须保证幂等。
if ("TRADE_SUCCESS".equals(tradeStatus)
        || "TRADE_FINISHED".equals(tradeStatus)) {
    response.getWriter().write("success");
} else {
    response.getWriter().write("failure");
}

更新订单时不能只看 trade_status。至少还要验证签名、app_id、订单号和金额。

查询订单是否已经创建

请求超时后,不要马上重新支付。网络超时只能说明调用方没有及时收到结果,不能证明支付宝没有处理这次请求。

可以通过 alipay.trade.query,按商户订单号查询:

AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("""
{
  "out_trade_no": "TEST202609090001"
}
""");

AlipayTradeQueryResponse response = alipayClient.execute(request);

System.out.println("code=" + response.getCode());
System.out.println("subCode=" + response.getSubCode());
System.out.println("tradeStatus=" + response.getTradeStatus());
System.out.println("body=" + response.getBody());

如果能够查到交易,应按当前交易状态继续处理,不要再创建同一笔订单。确认查不到交易后,再换用新的订单号测试。

建议的排查顺序

  1. 确认请求地址是沙箱网关。
  2. 确认 appid 和所有密钥来自同一个沙箱应用。
  3. 确认 SDK 中填写的是支付宝公钥,而不是应用公钥。
  4. 确认平台与代码使用相同的 sign_type,通常是 RSA2。
  5. 使用全新的 out_trade_no 和最小业务参数测试。
  6. 完整记录 HTTP 状态、响应体、code、sub_code、msg 和 sub_msg。
  7. 从实际部署机器测试沙箱网关的连通性。
  8. 请求超时后,调用 alipay.trade.query 核实交易状态。
  9. 检查 notifyUrl 是否能从公网访问,以及通知验签逻辑是否正确。
  10. 如果更换网络和时间后仍能稳定复现,且签名与参数都没有异常,再带上完整的请求时间和响应信息联系支付宝技术支持。

生产环境还需要设置合理的连接超时和读取超时,并在请求超时后查询订单状态。支付接口不能把“没有收到响应”直接视为“支付失败”,否则可能造成重复下单或订单状态不一致。

备注:内容仅供参考。