手机网站支付沙箱环境提示“系统繁忙,请稍后再试”如何调试?
结论
支付宝沙箱客户端显示“系统繁忙,请稍后再试”时,这通常只是一个兜底提示,无法直接说明故障原因。排查不能只盯着手机页面,而要检查服务端生成支付请求的完整过程,主要包括:
- 是否调用沙箱网关
https://openapi-sandbox.dl.alipaydev.com/gateway.do app_id、商户私钥和支付宝公钥是否来自同一个沙箱应用- 调用的接口是否为
alipay.trade.wap.pay - 签名方式、字符集和请求参数是否正确
out_trade_no是否重复,金额格式是否有效- 服务端返回的是支付表单还是接口错误
- 沙箱支付宝是否登录了沙箱买家账号
如果服务端已经收到明确的 code、sub_code 或 sub_msg,应根据这些字段处理。只有在服务端成功生成支付页面,而沙箱支付宝仍显示“系统繁忙”时,才需要继续排查客户端环境或沙箱服务。
先确认完整调用链
手机网站支付的正常流程如下:
- H5 页面请求商户服务端创建订单。
- 商户服务端调用
alipay.trade.wap.pay。 - 服务端生成支付宝支付表单或跳转页面。
- 浏览器提交表单并进入支付宝收银台。
- 支付完成后,支付宝浏览器跳转到
return_url。 - 支付宝服务端向
notify_url发送异步通知。 - 商户服务端通过
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_idmethodsign_typecharsettimestampbiz_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>转义成了<form> - 返回内容是否被反向代理、网关或前端框架截断
- 浏览器控制台和 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 为准,不要只根据客户端提示猜测原因。
建议的排查顺序
- 确认调用的是
alipay.trade.wap.pay,并且使用沙箱网关。 - 确认
app_id、商户私钥和支付宝公钥来自同一个沙箱应用。 - 使用新的订单号和最小金额重新创建订单。
- 记录 SDK 返回的原始响应和异常堆栈。
- 确认支付接口直接输出未经转义的 HTML 表单。
- 使用沙箱买家账号登录匹配的沙箱客户端。
- 通过
alipay.trade.query查询订单是否已经创建。 - 更换网络、重新登录或清理缓存,排除客户端状态问题。
- 如果请求参数和签名都正确,但沙箱仍持续返回没有明确信息的错误,应保留发生时间、
app_id、out_trade_no、接口响应和相关日志,再通过支付宝开放平台的支持渠道核查。
提交问题时,不要提供商户私钥或其他完整的敏感数据。相比“页面提示系统繁忙”,订单号、错误码、发生时间和脱敏后的请求参数更有助于定位问题。
备注:内容仅供参考。
