PAYATHON 2026

支付宝对接沙箱支付时支付页面打不开并提示SYSTEM_ERROR

支付阿杰

结论

SYSTEM_ERROR 是支付宝返回的通用系统异常。仅凭支付页面上的错误码,无法确定具体原因。在沙箱环境中,常见问题包括:

  • 沙箱网关、沙箱 app_id 和沙箱密钥没有配套使用;
  • 请求签名错误,或混淆了应用公钥与支付宝公钥;
  • alipay.trade.page.pay 的必填参数缺失或格式不正确;
  • 服务端没有将支付宝 SDK 返回的 HTML 表单正确输出到浏览器;
  • 支付宝沙箱服务暂时异常。

排查时,先核对环境和密钥,再记录完整的请求参数及支付宝返回内容。不要仅凭页面上的”系统有点儿忙”,就认定是支付宝服务器故障。

1. 检查是否使用了沙箱网关

电脑网站支付的沙箱网关通常应配置为:

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

正式环境网关为:

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

沙箱 app_id、沙箱应用私钥、沙箱支付宝公钥必须与沙箱网关配套使用。常见错误有:

  • 使用沙箱 app_id 请求正式网关;
  • 使用正式应用密钥请求沙箱网关;
  • 复制了其他沙箱应用的公钥;
  • 修改配置后,正在运行的服务仍在读取旧配置。

发起请求前,可以记录当前使用的网关和 app_id,但不要将私钥写入日志。

2. 核对密钥和签名配置

调用支付宝开放平台 API 时,需要分清以下三种密钥:

  • 应用私钥:保存在商户侧,用于生成请求签名;
  • 应用公钥:上传到支付宝开放平台;
  • 支付宝公钥:由支付宝提供,用于验证支付宝响应和异步通知。

初始化客户端时,应填写”应用私钥”和”支付宝公钥”,不能误将应用公钥填入支付宝公钥的位置。

同时确认以下配置保持一致:

sign_type = RSA2
charset   = UTF-8
format    = json

如果应用配置使用 RSA2,代码也必须使用 RSA2。密钥中的换行、空格或 PEM 头尾处理不正确,同样可能导致签名失败。

不同 SDK 对密钥格式的要求可能不同。部分 SDK 接受不带 PEM 头尾的密钥正文,部分封装组件则要求完整的 PEM 格式,应以实际使用的 SDK 文档为准。例如:

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

应用私钥不能保存在浏览器端,也不能由浏览器端使用。

3. 检查支付请求参数

电脑网站支付通常调用:

alipay.trade.page.pay

核心业务参数示例:

{
  "out_trade_no": "ORDER_20260913_0001",
  "product_code": "FAST_INSTANT_TRADE_PAY",
  "total_amount": "0.01",
  "subject": "沙箱支付测试订单"
}

需要重点检查:

  • out_trade_no 在商户系统中是否唯一;
  • total_amount 是否为合法的金额字符串;
  • subject 是否为空;
  • product_code 是否为 FAST_INSTANT_TRADE_PAY;
  • JSON 能否正常解析;
  • 参数中是否存在不可见字符或错误编码;
  • 订单号是否过长,或包含不合适的特殊字符。

排查时,先使用简单的字母、数字订单号和最小合法金额,减少业务数据造成的干扰:

out_trade_no=TEST202609130001
total_amount=0.01
subject=Sandbox Test

4. 正确输出 SDK 生成的支付表单

alipay.trade.page.pay 的页面支付流程通常不是让前端解析 JSON,而是由 SDK 生成一个自动提交的 HTML 表单,再由服务端将其原样返回给浏览器。

Java 示例:

AlipayClient alipayClient = new DefaultAlipayClient(
    "https://openapi.alipaydev.com/gateway.do",
    APP_ID,
    APP_PRIVATE_KEY,
    "json",
    "UTF-8",
    ALIPAY_PUBLIC_KEY,
    "RSA2"
);

AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl("https://example.com/alipay/return");
request.setNotifyUrl("https://example.com/alipay/notify");

request.setBizContent("""
{
  "out_trade_no": "TEST202609130001",
  "product_code": "FAST_INSTANT_TRADE_PAY",
  "total_amount": "0.01",
  "subject": "Sandbox Test"
}
""");

AlipayTradePagePayResponse response = alipayClient.pageExecute(request);

if (response.isSuccess()) {
    String form = response.getBody();
    // 将 form 作为 HTML 原样写入 HTTP 响应
} 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());
}

在 Servlet 中,可以这样返回表单:

response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(form);
response.getWriter().flush();

如果使用 Spring MVC,可以直接返回 HTML:

@GetMapping(value = "/pay", produces = "text/html;charset=UTF-8")
@ResponseBody
public String pay() throws AlipayApiException {
    AlipayTradePagePayResponse response = alipayClient.pageExecute(request);

    if (!response.isSuccess()) {
        throw new IllegalStateException(
            "Alipay request failed: code=" + response.getCode()
                + ", subCode=" + response.getSubCode()
                + ", msg=" + response.getMsg()
                + ", subMsg=" + response.getSubMsg()
        );
    }

    return response.getBody();
}

不要再次对 response.getBody() 进行 HTML 转义,否则 <form> 会变成普通文本,页面也无法自动跳转。

5. 查看服务端的完整响应

支付页面只显示 SYSTEM_ERROR 时,还要检查服务端日志中的以下字段:

code
msg
sub_code
sub_msg
request_id
trace_id

实际能获取哪些字段,取决于 SDK 版本和接口返回内容,不一定全部存在。

还应记录以下非敏感信息:

gateway
app_id
method
sign_type
charset
timestamp
out_trade_no
total_amount
SDK 返回的 code、sub_code、msg、sub_msg

以下内容严禁写入日志:

应用私钥
完整签名原文中的敏感业务数据
用户密码
支付密码
完整身份证号或银行卡号

如果服务端已经收到明确的 sub_code,应优先根据 sub_code 排查。页面上的 SYSTEM_ERROR 过于笼统,不能代替接口响应日志。

6. 检查回调地址和网络环境

return_url 用于支付完成后的浏览器跳转,notify_url 用于接收支付宝服务器发送的异步通知。它们通常不是决定支付页面能否打开的唯一因素,但配置异常仍可能影响整个支付流程。

需要确认:

  • URL 包含完整的 http:// 或 https://;
  • URL 中没有空格、中文引号等异常字符;
  • notify_url 可以从公网访问;
  • HTTPS 证书有效;
  • 防火墙或网关没有拦截支付宝请求;
  • 回调接口不会被登录验证、CSRF 校验或重定向拦截。

本地地址通常不能直接用作异步通知地址:

http://localhost:8080/alipay/notify
http://127.0.0.1:8080/alipay/notify

不过,异步通知地址暂时无法访问,通常不该作为支付页面完全打不开的首要判断依据。仍应先检查网关、签名和请求参数。

7. 排除浏览器和沙箱账号问题

如果接口已经成功生成支付表单,但跳转后才出现错误,可以继续检查:

  • 是否使用了支付宝沙箱提供的买家账号;
  • 是否误用正式支付宝账号登录沙箱收银台;
  • 浏览器是否拦截表单提交、重定向或 Cookie;
  • 页面是否在内嵌 WebView 中打开,从而产生兼容性问题;
  • 换用无痕窗口或其他浏览器后,问题是否仍能复现。

沙箱账号、密码和余额,应以支付宝开放平台沙箱页面当前显示的信息为准。

8. 判断是否为沙箱临时异常

完成以下检查后,如果同一套最小请求仍间歇性返回 SYSTEM_ERROR,才更有可能是沙箱环境暂时异常:

  • 网关已经确认是沙箱地址;
  • app_id 和密钥来自同一个沙箱应用;
  • 签名类型和字符编码一致;
  • 最小支付参数合法;
  • SDK 返回的 HTML 已原样输出;
  • 服务端没有返回更具体的 sub_code;
  • 更换订单号、浏览器和网络后,问题仍然存在。

这时可以稍后重试,并保存错误发生时间、app_id、接口名称、脱敏后的请求参数以及 request_id 等信息,方便进一步定位。重试时必须更换 out_trade_no,或者先确认原订单是否已经创建,以免重复订单干扰判断。

推荐排查顺序

  1. 确认网关是 https://openapi.alipaydev.com/gateway.do。
  2. 确认沙箱 app_id、应用私钥和支付宝公钥属于同一个应用。
  3. 确认使用 RSA2 和 UTF-8,并检查密钥格式。
  4. 使用最小参数调用 alipay.trade.page.pay。
  5. 检查 SDK 返回的 code、sub_code、msg 和 sub_msg。
  6. 将 SDK 返回的 HTML 表单原样输出到浏览器。
  7. 使用沙箱买家账号测试。
  8. 确认以上配置无误后,再考虑沙箱服务暂时异常。

不要反复修改业务参数来碰运气。服务端的完整响应通常比页面上的 SYSTEM_ERROR 更能说明问题所在。

备注:内容仅供参考。