PAYATHON 2026

更换新APPID后Android APP无法调起支付宝

支付阿杰

结论

这类问题通常不在 Android 调起代码本身,更可能是新 APPID 在支付宝生产环境中的应用状态、APP 支付能力、商户签约关系或应用信息配置尚未完全生效。

沙箱测试正常,只能证明基本调用流程和签名逻辑可用,并不表示新 APPID 已经具备生产环境收款条件。建议先确认新应用是否已上线,是否已经签约并开通 alipay.trade.app.pay 对应的 APP 支付能力,以及 Android 包名和应用签名是否已在新应用中正确配置并通过审核。

请求中的 notify_url 也是相对路径:

/userservice/v1/alipayVipOrderNotify

生产环境中应改为支付宝服务器可通过公网访问的完整 HTTPS 地址。它未必是当前报错的唯一原因,但需要修正。

优先检查新应用的生产状态

更换 APPID,相当于换了一个独立的支付宝应用。旧应用已经完成的产品签约、应用审核、商户绑定和安全配置,通常不会自动继承到新应用。

请在支付宝开放平台逐项检查:

  1. 新应用是否已提交审核,并处于可用或已上线状态。
  2. 新应用是否已经添加并开通 APP 支付产品。
  3. APP 支付签约是否已经生效,而不是仍处于申请、审核或待签约状态。
  4. 新 APPID 是否绑定到正确的商户账号。
  5. 公司主体变化后,新主体是否已经完成商户认证和相应的产品签约。
  6. Android 应用的包名和应用签名是否已配置到新应用,并通过审核。
  7. 当前调用是否使用生产网关、生产密钥和生产 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、网关及密钥相互独立,不能交叉使用。

推荐排查顺序

建议按以下顺序处理:

  1. 在开放平台确认新 APPID 已上线。
  2. 确认新主体已经完成 APP 支付签约,且产品状态可用。
  3. 确认 APPID、收款商户账号和主体之间的关系正确。
  4. 重新配置并核对 Android 包名及最终安装包签名。
  5. 将 notify_url 改为公网可访问的完整 HTTPS 地址。
  6. 使用新的 out_trade_no 和当前时间重新生成订单。
  7. 确保服务端生成的订单字符串未经修改,直接传入 PayTask.payV2()。
  8. 记录服务端响应中的 code、sub_code、msg 和 sub_msg。
  9. 记录 Android SDK 返回的 resultStatus、memo 和 result。
  10. 携带 APPID、接口调用时间、订单号、服务端错误码和客户端同步结果,向支付宝技术支持查询。

根据现有信息,无法只凭“系统繁忙”确定唯一原因。不过,从“旧 APPID 正常、新 APPID 在生产环境失败、沙箱正常”这些现象看,应先检查新应用的上线和签约状态、主体与商户的绑定关系,以及 Android 包名和签名配置。同时,也需要修正请求中的相对 notify_url。

备注:内容仅供参考。