更换新APPID后Android APP无法调起支付宝
结论
这类问题通常不在 Android 调起代码本身,更可能是新 APPID 在支付宝生产环境中的应用状态、APP 支付能力、商户签约关系或应用信息配置尚未完全生效。
沙箱测试正常,只能证明基本调用流程和签名逻辑可用,并不表示新 APPID 已经具备生产环境收款条件。建议先确认新应用是否已上线,是否已经签约并开通 alipay.trade.app.pay 对应的 APP 支付能力,以及 Android 包名和应用签名是否已在新应用中正确配置并通过审核。
请求中的 notify_url 也是相对路径:
/userservice/v1/alipayVipOrderNotify
生产环境中应改为支付宝服务器可通过公网访问的完整 HTTPS 地址。它未必是当前报错的唯一原因,但需要修正。
优先检查新应用的生产状态
更换 APPID,相当于换了一个独立的支付宝应用。旧应用已经完成的产品签约、应用审核、商户绑定和安全配置,通常不会自动继承到新应用。
请在支付宝开放平台逐项检查:
- 新应用是否已提交审核,并处于可用或已上线状态。
- 新应用是否已经添加并开通 APP 支付产品。
- APP 支付签约是否已经生效,而不是仍处于申请、审核或待签约状态。
- 新 APPID 是否绑定到正确的商户账号。
- 公司主体变化后,新主体是否已经完成商户认证和相应的产品签约。
- Android 应用的包名和应用签名是否已配置到新应用,并通过审核。
- 当前调用是否使用生产网关、生产密钥和生产 APPID,避免混用沙箱参数。
如果新应用尚未正式上线,或者 APP 支付产品还没有签约成功,即使 RSA2 签名完全正确,支付宝客户端也可能只显示“系统繁忙,请稍后再试”之类的通用提示。
检查 Android 包名和应用签名
新应用需要重新配置 Android 应用信息。旧 APPID 配置过,不代表新 APPID 会自动沿用。
需要重点核对:
- Android
applicationId,也就是实际安装包的包名。 - 正式 APK 或 AAB 使用的签名证书。
- 支付宝开放平台中登记的应用签名。
- 当前测试包是否使用 debug 签名,而平台登记的是 release 签名。
- 应用经过加固、渠道重签名或应用商店代签后,最终安装包的签名是否发生变化。
可以从实际安装包中提取签名信息,例如:
apksigner verify --print-certs app-release.apk
也可以使用 JDK 工具查看证书:
keytool -printcert -jarfile app-release.apk
应以最终安装到手机上的 APK 签名为准,不能只检查 Android Studio 中配置的 keystore。不同工具显示的签名格式可能不同。提交到平台前,还要确认平台要求的是证书指纹、MD5,还是经过特定转换的应用签名。
修正 notify_url
当前请求包含:
notify_url=%2Fuserservice%2Fv1%2FalipayVipOrderNotify
解码后为:
/userservice/v1/alipayVipOrderNotify
这是相对路径,支付宝服务器无法通过它找到通知接口。应改成公网可访问的完整地址,例如:
https://pay.example.com/userservice/v1/alipayVipOrderNotify
该地址还需要满足以下条件:
- 可以从公网直接访问。
- 不依赖登录态、Cookie 或前端鉴权。
- 不会被防火墙、网关白名单或 WAF 错误拦截。
- 能够接收支付宝规定的请求方式和参数格式。
- 验签成功并完成业务处理后,返回支付宝要求的成功响应。
服务端创建支付订单时,可以显式设置完整的回调地址:
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl(
"https://pay.example.com/userservice/v1/alipayVipOrderNotify"
);
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject("月套餐");
model.setBody("月套餐");
model.setOutTradeNo(outTradeNo);
model.setTotalAmount("0.02");
model.setProductCode("QUICK_MSECURITY_PAY");
model.setTimeoutExpress("2h");
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 orderInfo = response.getBody();
不能只判断是否生成了字符串。服务端返回的 code、sub_code、msg 和 sub_msg 都应完整记录。这些字段通常比支付宝客户端显示的通用提示更有助于排查问题。
获取 Android SDK 的同步返回结果
支付宝客户端页面显示“系统繁忙”,不代表系统没有返回详细错误信息。Android 端应记录 PayTask.payV2() 的完整返回结果,尤其是 resultStatus、memo 和 result。
Runnable payRunnable = () -> {
PayTask alipay = new PayTask(activity);
Map<String, String> result =
alipay.payV2(orderInfo, true);
String resultStatus = result.get("resultStatus");
String memo = result.get("memo");
String rawResult = result.get("result");
Log.i("Alipay", "resultStatus=" + resultStatus);
Log.i("Alipay", "memo=" + memo);
Log.i("Alipay", "result=" + rawResult);
activity.runOnUiThread(() -> {
// 根据 resultStatus 展示支付结果。
// 最终支付状态仍应由服务端异步通知或主动查询确认。
});
};
Thread payThread = new Thread(payRunnable);
payThread.start();
如果手头只有客户端截图,没有同步返回值和服务端接口响应,就很难判断失败是由应用权限、签约状态、订单参数还是风控校验引起的。
检查请求参数和订单字符串
示例请求中还有几个问题需要确认。
时间参数已经过期
示例中的时间是:
timestamp=2022-11-30 16:39:25
time_expire=2022-11-30 18:39:25
如果这只是历史日志,可以忽略。如果实际测试仍在复用旧订单字符串,支付宝会因请求时间或订单有效期不正确而拒绝请求。每次测试都应由服务端重新生成订单信息。
服务器时间也应通过 NTP 等方式保持准确,避免系统时间出现较大偏差。
订单号不能重复使用
out_trade_no 在当前商户订单体系中必须保持唯一。排查时,不要反复使用已经成功、关闭或创建过的订单号。
建议每次生成新的订单号:
String outTradeNo =
"VIP" + System.currentTimeMillis();
不要二次编码或重新拼接
服务端通过 sdkExecute() 生成订单字符串后,应原样交给 Android SDK:
Map<String, String> result =
new PayTask(activity).payV2(orderInfo, true);
客户端不要再次对整个 orderInfo 进行 URL 编码,也不要解析后重新排序或拼接参数,否则可能改变签名原文。
确认金额格式
total_amount 应使用两位小数,例如:
0.02
不建议使用 double 直接计算支付金额,以免产生浮点精度问题。服务端应使用 BigDecimal:
BigDecimal amount = new BigDecimal("0.02");
model.setTotalAmount(amount.setScale(2).toPlainString());
区分支付宝公钥和应用公钥
密钥配置中,以下三项最容易混淆:
- 应用私钥:商户服务端使用它生成签名。
- 应用公钥:上传到支付宝开放平台,供支付宝验证商户签名。
- 支付宝公钥:商户服务端使用它验证支付宝返回的数据和异步通知。
SDK 初始化参数中应填写支付宝公钥,而不是应用公钥:
new DefaultAlipayClient(
gateway,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
如果新应用启用了证书模式,就不能继续混用普通公钥模式的配置。此时需要使用对应的应用证书、支付宝公钥证书和支付宝根证书,并通过证书模式的 SDK 初始化方法进行配置。
沙箱和生产环境的 APPID、网关及密钥相互独立,不能交叉使用。
推荐排查顺序
建议按以下顺序处理:
- 在开放平台确认新 APPID 已上线。
- 确认新主体已经完成 APP 支付签约,且产品状态可用。
- 确认 APPID、收款商户账号和主体之间的关系正确。
- 重新配置并核对 Android 包名及最终安装包签名。
- 将
notify_url改为公网可访问的完整 HTTPS 地址。 - 使用新的
out_trade_no和当前时间重新生成订单。 - 确保服务端生成的订单字符串未经修改,直接传入
PayTask.payV2()。 - 记录服务端响应中的
code、sub_code、msg和sub_msg。 - 记录 Android SDK 返回的
resultStatus、memo和result。 - 携带 APPID、接口调用时间、订单号、服务端错误码和客户端同步结果,向支付宝技术支持查询。
根据现有信息,无法只凭“系统繁忙”确定唯一原因。不过,从“旧 APPID 正常、新 APPID 在生产环境失败、沙箱正常”这些现象看,应先检查新应用的上线和签约状态、主体与商户的绑定关系,以及 Android 包名和签名配置。同时,也需要修正请求中的相对 notify_url。
备注:内容仅供参考。
