PAYATHON 2026

手机网站支付沙箱环境提示“系统繁忙,请稍后再试”如何调试?

支付小周

结论

支付宝沙箱客户端显示“系统繁忙,请稍后再试”时,这通常只是一个兜底提示,无法直接说明故障原因。排查不能只盯着手机页面,而要检查服务端生成支付请求的完整过程,主要包括:

  • 是否调用沙箱网关 https://openapi-sandbox.dl.alipaydev.com/gateway.do
  • app_id、商户私钥和支付宝公钥是否来自同一个沙箱应用
  • 调用的接口是否为 alipay.trade.wap.pay
  • 签名方式、字符集和请求参数是否正确
  • out_trade_no 是否重复,金额格式是否有效
  • 服务端返回的是支付表单还是接口错误
  • 沙箱支付宝是否登录了沙箱买家账号

如果服务端已经收到明确的 code、sub_code 或 sub_msg,应根据这些字段处理。只有在服务端成功生成支付页面,而沙箱支付宝仍显示“系统繁忙”时,才需要继续排查客户端环境或沙箱服务。

先确认完整调用链

手机网站支付的正常流程如下:

  1. H5 页面请求商户服务端创建订单。
  2. 商户服务端调用 alipay.trade.wap.pay。
  3. 服务端生成支付宝支付表单或跳转页面。
  4. 浏览器提交表单并进入支付宝收银台。
  5. 支付完成后,支付宝浏览器跳转到 return_url。
  6. 支付宝服务端向 notify_url 发送异步通知。
  7. 商户服务端通过 alipay.trade.query 确认最终交易状态。

不要在前端 JavaScript 中保存商户私钥或直接生成签名。签名和支付请求必须由可信的服务端完成。

检查沙箱配置是否混用

沙箱环境和正式环境的配置不能混用。至少需要核对以下内容:

配置项沙箱环境要求
网关https://openapi-sandbox.dl.alipaydev.com/gateway.do
app_id沙箱应用的 App ID
商户私钥与沙箱应用上传的公钥配对
支付宝公钥沙箱应用对应的支付宝公钥
sign_type通常使用 RSA2,以应用实际配置为准
charset建议统一使用 UTF-8
买家账号使用沙箱提供的买家账号
支付客户端使用与沙箱环境匹配的客户端和登录方式

一种常见错误是使用正式应用的 app_id,同时配置沙箱密钥或沙箱网关。请求可能仍会进入支付宝页面,但后续校验会失败。

还要注意区分“应用公钥”和“支付宝公钥”:

  • 商户使用自己的私钥为请求签名。
  • 支付宝使用商户上传的应用公钥验签。
  • 商户使用支付宝公钥验证支付宝返回的数据。

如果应用使用证书模式,应采用证书模式对应的 SDK 配置,不能再套用普通公钥模式的初始化方法。

在服务端记录原始请求和响应

调试时需要记录:

  • 请求网关
  • app_id
  • method
  • sign_type
  • charset
  • timestamp
  • biz_content
  • SDK 返回的原始响应
  • 异常类型和完整堆栈
  • 订单号 out_trade_no

商户私钥、完整签名和用户密码等敏感信息,不能写入可公开访问的日志。

还要检查服务端究竟向浏览器返回了什么。正常情况下,pageExecute 会生成一段能够自动提交的 HTML 表单。如果返回的是错误 JSON、空字符串、经过转义的 HTML,或者业务系统在外面又包了一层 JSON,手机端就可能无法正常跳转。

Java SDK 调用示例

下面使用普通公钥模式演示。密钥内容和应用 ID 必须替换为同一沙箱应用的真实配置。

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.request.AlipayTradeWapPayRequest;

public class AlipaySandboxWapPay {

    private static final String GATEWAY =
            "https://openapi-sandbox.dl.alipaydev.com/gateway.do";

    private static final String APP_ID = "YOUR_SANDBOX_APP_ID";
    private static final String MERCHANT_PRIVATE_KEY =
            "YOUR_MERCHANT_PRIVATE_KEY";
    private static final String ALIPAY_PUBLIC_KEY =
            "YOUR_SANDBOX_ALIPAY_PUBLIC_KEY";

    public static String createPayForm() throws Exception {
        AlipayClient alipayClient = new DefaultAlipayClient(
                GATEWAY,
                APP_ID,
                MERCHANT_PRIVATE_KEY,
                "json",
                "UTF-8",
                ALIPAY_PUBLIC_KEY,
                "RSA2"
        );

        AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

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

        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        model.setOutTradeNo("TEST_" + System.currentTimeMillis());
        model.setSubject("沙箱支付测试订单");
        model.setTotalAmount("0.01");
        model.setProductCode("QUICK_WAP_WAY");

        request.setBizModel(model);

        return alipayClient.pageExecute(request).getBody();
    }
}

控制器应直接以 HTML 形式返回表单,不要把表单当成普通字符串放进 JSON:

@GetMapping(value = "/pay", produces = "text/html;charset=UTF-8")
@ResponseBody
public String pay() throws Exception {
    return AlipaySandboxWapPay.createPayForm();
}

如果必须让前端接收表单内容,可以把表单写入新页面后再提交。不过,这种方式更容易受 HTML 转义、安全策略和浏览器限制影响。调试时最好先让服务端直接输出 HTML。

检查业务参数

手机网站支付需要重点核对这些参数:

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

需要注意:

  • out_trade_no 在商户范围内必须唯一。重复测试时,应生成新的订单号,或者先查询并关闭原订单。
  • total_amount 应使用十进制金额字符串,例如 "0.01",不能包含货币符号。
  • subject 不能为空。
  • 手机网站支付的 product_code 应为 QUICK_WAP_WAY。
  • biz_content 必须是合法 JSON,不能有多余转义、错误引号或拼错的字段名。
  • 服务端时间应保持基本准确。时间偏差过大可能导致请求校验失败。
  • 请求参数在签名前后的字符编码必须一致。

判断失败发生在哪一层

尚未跳到支付宝页面

如果点击支付后仍停留在商户页面,应重点检查:

  • 创建订单接口是否返回 HTTP 200
  • 返回内容是否为完整的 HTML 表单
  • 页面是否把 <form> 转义成了 &lt;form&gt;
  • 返回内容是否被反向代理、网关或前端框架截断
  • 浏览器控制台和 Network 面板是否存在 JavaScript、CSP 或网络错误

可以先在普通桌面浏览器中访问支付接口,查看页面源代码中是否包含支付宝网关地址和自动提交表单。

已进入支付宝,但立即提示系统繁忙

此时应重点检查:

  • 网关、app_id 和密钥是否来自同一个沙箱环境
  • 请求是否使用 RSA2
  • 支付宝公钥是否复制完整,是否夹杂空格或错误换行
  • 沙箱买家账号是否正确
  • 订单号是否已经支付、关闭或重复
  • 服务端生成的请求参数是否为空或经过二次编码
  • 沙箱客户端的缓存、登录状态和网络环境是否异常

可以退出沙箱账号,清除客户端缓存后重新登录,再使用新的 out_trade_no 测试。客户端操作只能用于排除本地状态问题,不能替代服务端日志分析。

已支付,但业务系统仍显示未支付

这通常与“系统繁忙”不是同一个问题,需要检查:

  • notify_url 是否可以由支付宝服务器从公网访问
  • 是否使用 HTTPS 和有效证书
  • 是否正确验证异步通知签名
  • 是否校验 app_id、seller_id、out_trade_no 和金额
  • 处理成功后是否按照接口要求返回成功响应
  • 是否通过 alipay.trade.query 主动查询订单状态

return_url 只用于浏览器跳转,不能作为最终到账依据。最终交易状态应以验签通过的异步通知或主动查询结果为准。

使用订单查询进一步确认

支付页面只显示模糊错误时,可以使用创建订单时的 out_trade_no 调用 alipay.trade.query。

import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;

AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
queryRequest.setBizContent(
        "{\"out_trade_no\":\"TEST_202609090001\"}"
);

AlipayTradeQueryResponse response = alipayClient.execute(queryRequest);

System.out.println("code=" + response.getCode());
System.out.println("msg=" + response.getMsg());
System.out.println("subCode=" + response.getSubCode());
System.out.println("subMsg=" + response.getSubMsg());
System.out.println("body=" + response.getBody());

查询结果可以帮助判断:

  • 订单根本没有创建成功
  • 订单已经存在但尚未支付
  • 订单已支付
  • 订单已关闭
  • 请求因签名或权限问题被拒绝

处理具体错误时,应以实际返回的 code、sub_code 和 sub_msg 为准,不要只根据客户端提示猜测原因。

建议的排查顺序

  1. 确认调用的是 alipay.trade.wap.pay,并且使用沙箱网关。
  2. 确认 app_id、商户私钥和支付宝公钥来自同一个沙箱应用。
  3. 使用新的订单号和最小金额重新创建订单。
  4. 记录 SDK 返回的原始响应和异常堆栈。
  5. 确认支付接口直接输出未经转义的 HTML 表单。
  6. 使用沙箱买家账号登录匹配的沙箱客户端。
  7. 通过 alipay.trade.query 查询订单是否已经创建。
  8. 更换网络、重新登录或清理缓存,排除客户端状态问题。
  9. 如果请求参数和签名都正确,但沙箱仍持续返回没有明确信息的错误,应保留发生时间、app_id、out_trade_no、接口响应和相关日志,再通过支付宝开放平台的支持渠道核查。

提交问题时,不要提供商户私钥或其他完整的敏感数据。相比“页面提示系统繁忙”,订单号、错误码、发生时间和脱敏后的请求参数更有助于定位问题。

备注:内容仅供参考。