沙箱环境使用orderStr唤起APP支付提示系统繁忙
结论
成功生成 orderStr,只说明服务端完成了支付参数的组装和签名,不代表支付宝沙箱已经受理订单。遇到“系统繁忙,请稍后再试”,通常需要检查以下问题:
- 客户端使用的是正式版支付宝,而非沙箱配套客户端。
app_id、应用私钥、支付宝公钥或证书并非来自同一个沙箱应用。- 服务端生成
orderStr后又做了 URL 编码、解码、拼接或转义,导致签名失效。 biz_content中的订单号、金额、产品码等参数不符合 APP 支付要求。- 客户端没有将完整的
orderStr原样传给支付宝支付 SDK。 - 沙箱 APP 支付链路或测试工具出现异常。
电脑网站支付正常,只能说明网页支付的相关配置基本可用,无法证明 APP 支付的客户端、签名和产品权限配置正确。建议先通过官方服务端 SDK 生成订单串,再使用真实移动端支付 SDK 验证。API Explorer 的链路测试结果不能作为判断 APP 支付是否可用的唯一依据。
第一步:确认客户端和环境一致
服务端请求、客户端钱包和登录账号必须全部使用沙箱环境。逐项确认:
app_id是沙箱应用的 App ID。- 签名使用该沙箱应用对应的应用私钥。
- 验签使用沙箱控制台提供的支付宝公钥,或同一应用配置下的证书。
- 手机端安装并使用沙箱要求的支付宝客户端。
- 登录沙箱买家账号,而不是正式支付宝账号。
- 服务端没有混用正式环境的网关、密钥、证书或应用配置。
使用开放平台 SDK 时,网关应明确指向沙箱地址:
https://openapi-sandbox.dl.alipaydev.com/gateway.do
不能只切换网关,却继续使用正式应用的 app_id、密钥或证书。
第二步:检查 APP 支付的服务端调用方式
APP 支付应由服务端调用 alipay.trade.app.pay,再通过 SDK 的 sdkExecute 一类方法生成客户端所需的订单串。生成后,应将 orderStr 作为普通字符串直接交给移动端,服务端不要再修改其内容。
以下是 Java SDK 的典型写法。不同 SDK 版本或证书模式使用的类名和构造参数可能略有差异:
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi-sandbox.dl.alipaydev.com/gateway.do",
sandboxAppId,
appPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl("https://example.com/alipay/notify");
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(outTradeNo);
model.setTotalAmount("0.01");
model.setSubject("沙箱APP支付测试");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"生成支付订单失败,code=" + response.getCode()
+ ", subCode=" + response.getSubCode()
+ ", msg=" + response.getMsg()
+ ", subMsg=" + response.getSubMsg()
);
}
String orderStr = response.getBody();
还需要检查:
product_code使用QUICK_MSECURITY_PAY。out_trade_no在商户侧保持唯一,每次重复测试时都重新生成。total_amount是字符串形式的合法金额,例如"0.01"。subject不为空。charset、sign_type与实际签名配置一致。notify_url是支付宝可以访问的公网 HTTPS 地址。不过,异步通知地址不可达通常不会阻止客户端拉起支付页。- 完整返回
response.getBody(),不能只返回其中的biz_content或sign。
如果应用使用证书模式,就应采用对应的证书客户端初始化方式,不能混用公钥模式和证书模式的参数。
第三步:保证 orderStr 原样传输
orderStr 已经包含编码和签名后的参数。生成后再次调用 URLEncoder.encode(),或者由客户端解析后重新拼接,都会带来问题。
错误示例:
String orderStr = response.getBody();
String encodedOrderStr = URLEncoder.encode(orderStr, StandardCharsets.UTF_8);
return encodedOrderStr;
应直接返回原始订单串:
String orderStr = response.getBody();
return orderStr;
通过 JSON 接口下发时,可以这样封装:
{
"orderStr": "app_id=...&biz_content=...&charset=UTF-8&..."
}
JSON 序列化时出现转义不一定有问题,但客户端取出字段后,必须还原为原始字符串。尤其要检查以下字符是否被替换、丢失或重复编码:
&
=
%
+
/
可以分别在服务端生成订单串后、客户端调用支付 SDK 前记录:
- 字符串长度。
- SHA-256 摘要。
- 脱敏后的首尾内容。
如果两端记录的长度或摘要不同,说明订单串在传输过程中发生了变化。
第四步:检查移动端 SDK 调用
Android 端应将完整的 orderStr 直接传给支付 SDK:
final String orderInfo = orderStr;
Runnable payRunnable = () -> {
PayTask alipay = new PayTask(activity);
Map<String, String> result = alipay.payV2(orderInfo, true);
activity.runOnUiThread(() -> {
String resultStatus = result.get("resultStatus");
String memo = result.get("memo");
String resultData = result.get("result");
Log.d("Alipay", "resultStatus=" + resultStatus);
Log.d("Alipay", "memo=" + memo);
Log.d("Alipay", "result=" + resultData);
});
};
Thread payThread = new Thread(payRunnable);
payThread.start();
除了记录支付页面上的“系统繁忙”,还要保存 SDK 返回的以下字段:
resultStatusmemoresult
定位客户端支付结果时,resultStatus 更有参考价值。页面提示往往是统一的错误文案,无法直接判断问题来自签名、账号、网络还是沙箱服务。
同时确认项目集成的是官方支付 SDK,并排查这些情况:
- SDK 版本过旧。
- 项目中混入多个不同版本的支付 SDK。
- 客户端截断了较长的
orderStr。 - 前端框架或跨端插件对字符串进行了二次编码。
- 使用 WebView 打开
orderStr,而不是调用原生支付 SDK。 - Android 包配置或 iOS 回跳配置不完整。
第五步:核对密钥和签名配置
密钥错误是“可以得到订单串,但拉起后失败”的常见原因。sdkExecute 能在本地完成参数组装和签名,因此即使私钥配置错误,也可能生成一个格式完整的 orderStr。
需要逐项确认:
- 应用私钥确实与当前沙箱应用上传的应用公钥对应。
alipayPublicKey填写的是支付宝公钥,而不是应用公钥。- 沙箱配置中没有复制正式环境的密钥。
sign_type设置为RSA2时,代码实际使用的也是RSA2签名。- 私钥格式符合当前 SDK 的要求,没有缺行、多余空格或复制不完整的问题。
- 如果近期更换过密钥,运行中的服务已经重新加载配置。
- 多实例部署时,所有实例使用相同且正确的沙箱配置。
排查时可以创建一个全新的 out_trade_no,再由服务端生成新的 orderStr。不要反复使用历史订单串,否则订单状态、订单号重复或订单超时可能干扰判断。
第六步:正确看待 API Explorer 和链路测试
API Explorer 更适合验证接口参数和服务端请求,不能代替真实的移动端支付 SDK 环境。对于 alipay.trade.app.pay,即使工具已经返回响应,仍需将完整订单串交给移动端 SDK,才能验证后续支付流程。
如果 API Explorer 显示“没有数据”,或链路测试一直没有响应,可以按下面的方法区分问题:
- 在浏览器开发者工具中确认请求是否真正发出。
- 查看 HTTP 状态码和接口原始响应。
- 检查响应中是否有
code、sub_code、msg、sub_msg。 - 更换全新的订单号后再次测试。
- 确认 API Explorer 当前选择的是沙箱应用和沙箱环境。
- 关闭可能拦截请求的浏览器扩展、代理或企业网络限制,然后重试。
如果 sdkExecute 能返回订单串,但 API Explorer 或沙箱链路测试页面始终没有反应,同时电脑网站支付测试正常,不能直接认定是业务代码错误。链路测试页面本身也可能存在兼容性问题或临时服务异常。
推荐的最小化排查方法
搭建一条绕过业务前端和中间网关的最小测试链路:
- 使用官方服务端 SDK。
- 配置确认无误的沙箱
app_id和对应密钥。 - 将金额固定为
"0.01"。 - 每次生成新的
out_trade_no。 - 直接获取
response.getBody()。 - 通过 USB 调试或本地测试接口,把订单串交给原生移动端 Demo。
- 使用沙箱客户端和沙箱买家账号支付。
- 保存服务端响应,以及移动端返回的
resultStatus、memo。
如果这条最小链路可以完成支付,问题通常出在原业务系统的订单串传输、客户端封装或环境配置中。如果最小链路仍然提示系统繁忙,而且已经确认应用、密钥、账号和客户端都属于同一个沙箱环境,那么沙箱侧异常的可能性更大。
仍无法解决时需要提交的信息
向支付宝开放平台技术支持反馈时,不要只提交“系统繁忙”的截图。建议同时提供:
- 沙箱
app_id。 - 接口名称
alipay.trade.app.pay。 - 问题发生的准确时间和时区。
out_trade_no。- SDK 返回的
code、sub_code、msg、sub_msg。 - 移动端返回的
resultStatus和memo。 - 支付页面的错误截图。
- 服务端使用的语言及 SDK 版本。
- 手机系统、支付 SDK 版本和沙箱客户端版本。
- 当前使用的是公钥模式还是证书模式。
- 脱敏后的完整请求参数。
不要提交应用私钥、完整的证书私钥或未经脱敏的敏感数据。
注意事项
- 生成
orderStr不代表支付宝已经创建交易。 - APP 支付和电脑网站支付使用不同的产品链路。网页支付正常,仍不能排除 APP 支付的配置问题。
- 不要手动修改、排序或二次编码 SDK 生成的
orderStr。 - 支付结果应以服务端异步通知和主动查询结果为准,不能只看客户端显示。
- 沙箱环境可能临时不稳定。确认配置无误后,应记录问题发生时间、订单号和返回信息,再提交平台排查,不要不断使用同一个订单重复测试。
备注:内容仅供参考。
