app支付沙箱环境提示系统繁忙(ALIN10146)如何解决?
结论
ALIN10146 通常是支付宝客户端显示的通用失败提示,仅凭这个错误码无法确定具体原因。在沙箱 App 支付中,常见原因包括:
app_id、网关、应用私钥和支付宝公钥并非来自同一套沙箱配置;- 服务端生成的订单字符串缺少参数,或客户端对订单字符串进行了再次编码、拼接或修改;
- 签名方式、字符集或密钥格式配置有误;
- 混用了正式环境的买家账号、应用配置或网关;
- 沙箱环境暂时异常。
建议先查看支付宝开放平台的沙箱日志,以及服务端调用 alipay.trade.app.pay 时返回的原始响应。服务器端的异步通知验签发生在支付之后,通常不会直接导致客户端弹出 ALIN10146。
优先检查环境配置
沙箱支付使用的所有配置都必须来自同一个沙箱应用,不能与正式环境参数混用:
网关:https://openapi.alipaydev.com/gateway.do
app_id:沙箱应用 APPID
应用私钥:与沙箱应用公钥匹配的私钥
支付宝公钥:沙箱应用页面提供的支付宝公钥
sign_type:RSA2
charset:UTF-8
这里需要分清两个概念:
- 应用私钥:用于给商户请求签名。
- 支付宝公钥:用于验证支付宝通知或响应的签名。
支付宝公钥不是应用公钥,也不能从应用私钥中直接复制。密钥使用错误时,即使其他参数看起来正常,请求也可能无法通过校验。
如果采用证书模式,应用证书、支付宝公钥证书和支付宝根证书必须配套使用,不能和普通公钥模式混在一起。
正确生成 App 支付订单字符串
订单参数和签名应由服务端生成。客户端只需把服务端返回的完整订单字符串交给支付宝 SDK,不要自行重新签名。
下面是 Java 服务端使用普通公钥模式生成订单字符串的示例:
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
public class AlipayOrderService {
private static final String GATEWAY =
"https://openapi.alipaydev.com/gateway.do";
private static final String APP_ID =
"你的沙箱APPID";
private static final String APP_PRIVATE_KEY =
"你的应用私钥";
private static final String ALIPAY_PUBLIC_KEY =
"沙箱环境的支付宝公钥";
public String createOrder() throws AlipayApiException {
AlipayClient alipayClient = new DefaultAlipayClient(
GATEWAY,
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2"
);
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject("测试商品");
model.setOutTradeNo(createUniqueTradeNo());
model.setTotalAmount("0.01");
model.setProductCode("QUICK_MSECURITY_PAY");
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setBizModel(model);
request.setNotifyUrl("https://example.com/alipay/notify");
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"生成支付订单失败,code=" + response.getCode()
+ ", subCode=" + response.getSubCode()
+ ", msg=" + response.getMsg()
+ ", subMsg=" + response.getSubMsg()
);
}
return response.getBody();
}
private String createUniqueTradeNo() {
return "TEST" + System.currentTimeMillis();
}
}
服务端必须把 response.getBody() 原样返回给客户端,不要进行 URL 解码、拆分重组或二次签名。
Android 客户端调用示例:
final String orderInfo = orderStringFromServer;
Runnable payRunnable = () -> {
PayTask payTask = new PayTask(activity);
Map<String, String> result = payTask.payV2(orderInfo, true);
String resultStatus = result.get("resultStatus");
String memo = result.get("memo");
String rawResult = result.get("result");
Log.d("Alipay", "resultStatus=" + resultStatus);
Log.d("Alipay", "memo=" + memo);
Log.d("Alipay", "result=" + rawResult);
};
Thread payThread = new Thread(payRunnable);
payThread.start();
除了弹窗里的 ALIN10146,还要完整记录 payV2() 返回的 resultStatus、memo 和 result。生产日志中不宜长期保留完整订单字符串、签名或用户隐私数据。
按顺序排查
1. 检查服务端原始响应
SDK 调用失败后,不要继续向客户端返回空字符串或异常内容。至少应记录:
response.getCode();
response.getSubCode();
response.getMsg();
response.getSubMsg();
response.getBody();
真正有助于定位问题的通常是 subCode 和 subMsg,而不是客户端显示的通用提示。
2. 核对必要业务参数
App 支付通常至少需要正确设置以下参数:
subject
out_trade_no
total_amount
product_code=QUICK_MSECURITY_PAY
需要注意:
out_trade_no必须在商户系统内保持唯一。重复测试时,不要一直使用同一个订单号。total_amount应是合法的十进制金额字符串,例如"0.01"。subject不能为空。product_code应设置为QUICK_MSECURITY_PAY。
不要手工拼接 JSON 后随意进行 URL 编码。应优先使用支付宝服务端 SDK 构造请求。
3. 检查订单字符串是否被修改
可以分别记录以下信息:
- 服务端
response.getBody()的长度和摘要; - 客户端收到字符串后的长度和摘要;
- 调用
payV2()前字符串的长度和摘要。
如果订单字符串经过 JSON、表单或数据库传递,要确认 &、+、%、引号和换行没有被转义或替换。常见错误包括:
把完整订单字符串再次 URLEncoder.encode()
把字符串按 & 拆开后重新排序
将 + 解析为空格
复制时引入换行
对 sign 字段进行二次编码
4. 检查密钥格式
配置文件中的私钥通常应保存为连续的 Base64 内容。是否保留 PEM 头尾,取决于当前 SDK 和密钥加载方式。检查时尤其要注意:
- 是否误用了应用公钥;
- 是否使用了其他应用的私钥;
- 是否混入空格、换行或不可见字符;
- 密钥类型是否符合当前 SDK 的要求;
- 配置的
RSA2是否与实际签名方式一致。
不要为了所谓的兼容性同时尝试多套密钥。应先在沙箱后台确认当前应用公钥,再核对本地与之对应的私钥。
5. 使用沙箱买家账号
沙箱交易应使用开放平台沙箱页面提供的买家账号进行测试。普通的正式支付宝账号不一定能完成沙箱交易。
还需确认:
- 支付宝客户端可以正常拉起;
- 沙箱账号状态正常;
- 当前测试配置属于对应的沙箱应用;
- 手机系统时间准确;
- 网络未受到代理、抓包证书或安全软件的干扰。
6. 检查 SDK 集成
如果服务端生成的订单没有问题,但客户端仍然失败,可以继续检查:
- 支付宝 App 支付 SDK 是否完整集成;
- 项目中是否同时存在多个不同版本的支付宝 SDK;
- 混淆规则是否有遗漏;
- Android
Activity和线程调用方式是否符合 SDK 接入要求; - iOS 的 URL Scheme 和回调是否配置正确;
- 客户端是否误用 Demo 中的固定订单或旧订单替换了测试订单。
不要长期使用 SDK Demo 中的示例密钥、固定订单号或本地签名代码。Demo 只用于说明调用流程,实际应用应由服务端统一生成订单和签名。
验签应如何处理
异步通知验签使用支付宝公钥,而不是应用公钥:
boolean verified = AlipaySignature.rsaCheckV1(
request.getParameterMap(),
ALIPAY_PUBLIC_KEY,
"UTF-8",
"RSA2"
);
验签通过后,还需要核对:
app_id
out_trade_no
total_amount
seller_id(如果业务中有保存和校验)
trade_status
不能把客户端返回的支付成功直接作为最终入账依据。最终结果应以服务端查询结果,或验签通过的支付宝异步通知为准。
异步通知验签发生在支付请求被受理之后。如果付款页面还没有正常打开就出现 ALIN10146,应先检查环境配置、订单参数和请求签名,不必把排查重点放在通知验签代码上。
如何判断是否为沙箱临时异常
只有同时满足以下条件,才更有可能是沙箱服务暂时异常:
- 服务端可以成功生成完整的订单字符串;
- 沙箱 APPID、网关和密钥已经确认一致;
- 测试时使用了沙箱买家账号;
- 使用同一组订单参数重新生成新订单号后,仍然支付失败;
- SDK 返回内容中没有更具体的业务错误;
- 开放平台请求日志中没有参数或签名错误。
这时可以等待一段时间,使用新的 out_trade_no 再次测试,并查看支付宝开放平台的接口调用日志或服务状态。如果仍无法定位,可向支付宝技术支持提供请求时间、沙箱 APPID、接口名称、订单号和平台返回的错误信息,但不要提交应用私钥。
大多数 ALIN10146 问题都可以通过服务端原始响应或开放平台调用日志找到原因。先确认请求是否正确生成并被支付宝受理,再检查客户端和异步通知,可以避免把通用错误提示误判为验签问题。
备注:内容仅供参考。
