App支付唤起支付宝出现6001错误如何排查?
结论
支付宝 App 支付返回 6001,通常说明用户取消了支付,例如点击返回键、关闭收银台,或支付过程中 App 被中断。6001 不是 alipay.trade.app.pay 的服务端参数错误码,不能据此认定某个业务参数校验失败。
这份请求参数混入了许多示例字段,还存在过期时间、支付渠道配置冲突和编码过于复杂等问题。建议先把 biz_content 精简到 App 支付所需的基本字段,重新生成签名和订单字符串,然后确认客户端将订单字符串原样传给支付宝 SDK。
请求中需要重点检查的问题
1. time_expire 已经过期
请求时间是:
timestamp=2021-08-23 19:25:20
订单的绝对过期时间却是:
time_expire=2016-12-31 10:05:00
如果这些内容不是脱敏示例,而是实际提交的参数,那么订单创建时就已经过期。应删除该字段,或将其改为未来时间。
一般只需保留相对超时时间:
"timeout_express": "90m"
如果必须使用 time_expire,需要在每次创建订单时动态生成,不能直接照搬示例日期。
2. 支付渠道配置互相冲突
请求中同时出现了:
"enable_pay_channels": "pcredit,moneyFund,debitCardExpress",
"disable_pay_channels": "pcredit,moneyFund,debitCardExpress",
"specified_channel": "pcredit"
同一批渠道既被启用,又被禁用,配置本身互相冲突。排查时应先删除这些字段。确实有业务需求时,再根据签约能力选择一种渠道限制方式。
3. biz_content 包含大量非必要参数
请求还携带了花呗分期、行业回流、周期扣款签约、子商户和外部用户信息等字段,例如:
hb_fq_num
industry_reflux_info
agreement_sign_params
period_rule_params
sub_merchant
ext_user_info
这些字段不是普通 App 支付的必需参数,其中一部分还要求签约特定产品,或只适用于特定业务场景。直接复制完整示例,容易出现参数不适用、嵌套 JSON 转义错误或权限校验失败。
排查时先保留以下字段即可:
{
"out_trade_no": "唯一订单号",
"total_amount": "9.00",
"subject": "订单标题",
"product_code": "QUICK_MSECURITY_PAY",
"timeout_express": "30m"
}
确认最小请求能够正常支付后,再逐项添加可选字段。
4. 字符集和 URL 编码需要保持一致
当前请求使用:
charset=GBK
与此同时,biz_content 中包含百分号编码后的中文、嵌套 JSON 和多层转义。签名前后的字符集或编码次数只要不一致,就可能导致验签失败。
建议统一改用 UTF-8:
charset=UTF-8
订单字符串应由支付宝服务端 SDK 生成。客户端拿到订单字符串后,必须原样传给支付 SDK,不要执行以下操作:
- 不要再次进行 URL 编码;
- 不要先做 URL 解码,再重新编码;
- 不要手动把
+替换为空格; - 不要重新排列参数;
- 不要修改
biz_content; - 不要在客户端重新拼接签名参数。
URL 编码后的 %2B、+ 和空格含义不同。签名值中的 + 如果经过错误的表单解码,可能会变成空格。
5. 示例字段之间存在数据不一致
订单总金额是:
"total_amount": "9.00"
商品明细中的单价却是:
"price": 2000,
"quantity": 1
如果提交 goods_detail,商品金额与订单总金额应满足当前接口及业务场景的校验要求。排查时可以先删除整个 goods_detail,避免无关字段影响结果。
还要确认手机号、身份证号、子商户号和服务商 PID 等信息均来自真实业务配置,而不是文档中的示例值。
推荐的服务端调用方式
Java 服务端可以通过官方 SDK 生成 App 支付订单字符串。私钥只能保存在服务端,不能放入 App。
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2"
);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl("https://example.com/alipay/notify");
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", createUniqueOrderNo());
bizContent.put("total_amount", "9.00");
bizContent.put("subject", "测试订单");
bizContent.put("product_code", "QUICK_MSECURITY_PAY");
bizContent.put("timeout_express", "30m");
request.setBizContent(bizContent.toJSONString());
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"生成支付宝订单失败: code=" + response.getCode()
+ ", subCode=" + response.getSubCode()
+ ", msg=" + response.getMsg()
+ ", subMsg=" + response.getSubMsg()
);
}
String orderInfo = response.getBody();
response.getBody() 是需要交给客户端的完整订单字符串。不要只传 biz_content,也不要在服务端或客户端再次处理这个字符串。
Android 客户端调用示例
Runnable payRunnable = () -> {
PayTask payTask = new PayTask(activity);
// orderInfo 必须是服务端 sdkExecute 返回的完整字符串
Map<String, String> result = payTask.payV2(orderInfo, true);
String resultStatus = result.get("resultStatus");
String memo = result.get("memo");
String resultData = result.get("result");
runOnUiThread(() -> {
if ("9000".equals(resultStatus)) {
// 这里只表示客户端支付流程返回成功。
// 最终支付状态仍需由服务端异步通知或主动查询确认。
} else if ("6001".equals(resultStatus)) {
// 用户取消或支付流程被中断
} else {
// 记录 resultStatus、memo 和服务端订单号,继续排查
}
});
};
Thread payThread = new Thread(payRunnable);
payThread.start();
如果客户端每次都立即返回 6001,需要检查:
- 是否成功调起了支付宝收银台;
orderInfo是否为空、被截断或经过 JSON 转义;- 是否将订单字符串放入 URL 后又编码了一次;
- Activity 是否在调用后立即关闭或重建;
- 是否重复发起支付,覆盖了上一次支付流程;
- 是否在支付宝返回前主动关闭了支付页面;
- App 的生命周期、路由或登录逻辑是否中断了支付宝回跳。
建议的排查顺序
第一步:使用最小参数重新下单
只提交:
{
"out_trade_no": "TP202609090001",
"total_amount": "9.00",
"subject": "测试订单",
"product_code": "QUICK_MSECURITY_PAY",
"timeout_express": "30m"
}
每次测试都必须使用新的 out_trade_no。
第二步:统一使用 UTF-8
服务端 SDK、业务 JSON、HTTP 响应和日志全部使用 UTF-8,然后重新生成签名。修改任何参数后,都不能继续使用原来的 sign。
第三步:对比传输前后的订单字符串
分别记录:
- 服务端
sdkExecute()的返回值; - App 收到的字符串;
- 传入
payV2()前的字符串。
这三处内容应完全一致。记录日志时,需要隐藏签名和用户证件信息等敏感数据。
第四步:记录完整的客户端结果
至少记录以下字段:
resultStatus
memo
result
out_trade_no
不要只记录 6001。如果同时出现 4000、6002 或服务端 sub_code,这些信息通常更有助于判断是参数、网络还是签名出了问题。
第五步:由服务端查询订单状态
客户端返回 6001,不代表订单一定没有支付。网络中断或回跳失败也可能造成客户端结果与实际交易状态不一致。服务端应根据 out_trade_no 调用交易查询接口,或等待支付宝异步通知,以确认最终状态。
注意事项
6001通常表示支付流程被取消,不能将它视为明确的参数校验错误。- 不能直接复用问题中的
sign。任何字符、时间或业务字段发生变化后,都要重新签名。 - 支付宝私钥必须保存在服务端。
- 验证异步通知时,应使用支付宝公钥,并校验
app_id、订单号、金额和商户身份。 - 客户端显示支付成功不能代替服务端确认。发货或记账应以验签通过的异步通知或主动查询结果为准。
- 如果最小参数可以正常支付,再逐个恢复花呗分期、渠道限制或扩展参数,以确定具体是哪个字段引发了问题。
备注:内容仅供参考。
