PAYATHON 2026

easy-SDKPayment.App().pay 支付提示商家订单参数异常

支付小周

结论

Payment.App().pay() 只负责在服务端本地组装并签名 App 支付订单,不会立即向支付宝服务器发起请求。因此,这个接口没有报错,不代表订单参数、签名和应用配置一定正确。

出现“商家订单参数异常,请重新发起付款”时,通常需要检查以下问题:

  1. 客户端没有原样使用 response.getBody(),对内容进行了二次 URL 编码、解码或 JSON 转义。
  2. app_id、签名私钥和支付宝后台应用不匹配。
  3. 应用没有开通 App 支付能力,或者使用了错误的环境或应用。
  4. 签名模式配置有误,例如混用了普通公钥模式和证书模式。
  5. biz_content 中的金额、订单号或产品码不符合要求。

只看 AppID 2021002191650348 无法判断后台配置是否正确,还需要登录对应的支付宝开放平台账号,核对应用状态和支付能力。

首先检查客户端传参

服务端返回的 body 是完整的支付宝订单字符串,不是普通 JSON。客户端应将它作为一个完整字符串直接传给支付宝 SDK,不能拆分或修改。

服务端可以这样返回:

AlipayTradeAppPayResponse response =
        Factory.Payment.App().pay(subject, outTradeNo, totalAmount);

if (!response.isSuccess()) {
    throw new IllegalStateException(
            "生成支付参数失败,code=" + response.getCode()
                    + ", msg=" + response.getMsg()
                    + ", subCode=" + response.getSubCode()
                    + ", subMsg=" + response.getSubMsg()
    );
}

String orderInfo = response.getBody();
return orderInfo;

Android 端直接调用:

PayTask payTask = new PayTask(activity);
Map<String, String> result = payTask.payV2(orderInfo, true);

不要对 orderInfo 执行以下操作:

URLDecoder.decode(orderInfo, "UTF-8");
URLEncoder.encode(orderInfo, "UTF-8");
orderInfo.replace("\\", "");
orderInfo.split("&");

如果通过 JSON 接口传输,可以把订单字符串放在一个字段中,但客户端完成 JSON 反序列化后,必须取出原始字符串:

{
  "orderInfo": "app_id=...&biz_content=...&sign=...&sign_type=RSA2&..."
}

检查时尤其要注意:

  • sign 中的 %2B 是否变成了 + 或空格。
  • %2F、%3D 等编码是否被再次编码成 %252F、%253D。
  • 字符串两端是否多了双引号。
  • 网关、前端框架或客户端是否自动执行了 URL 编解码。
  • 传给 payV2() 的是否为 orderInfo,而不是整个 JSON 响应。

最直接的排查方法,是分别记录服务端生成的 response.getBody() 和客户端调用 SDK 前的 orderInfo,然后逐字符比较。两段内容应当完全一致。

核对 Easy SDK 初始化配置

使用普通公钥模式时,配置关系应类似下面这样:

Config config = new Config();
config.protocol = "https";
config.gatewayHost = "openapi.alipay.com";
config.signType = "RSA2";

config.appId = "2021002191650348";
config.merchantPrivateKey = "应用私钥";
config.alipayPublicKey = "支付宝公钥";

config.notifyUrl = "https://example.com/alipay/notify";

Factory.setOptions(config);

需要分清以下配置:

  • merchantPrivateKey 是应用私钥,应当与开放平台中该应用配置的公钥配对。
  • alipayPublicKey 是支付宝提供的支付宝公钥。
  • “应用公钥”和“支付宝公钥”是两把不同的密钥。
  • 如果后台配置的是应用公钥证书,就要使用 SDK 对应的证书模式,不能继续按照普通公钥模式初始化。
  • signType 通常需要与后台配置一致,例如 RSA2。

SDK 在本地生成订单参数时,只要能够解析私钥文本,就可能正常返回 body。但如果该私钥不属于当前 app_id,支付宝客户端在校验订单时仍会拒绝支付。

可以按以下步骤重新验证密钥关系:

  1. 从服务端当前使用的应用私钥导出公钥。
  2. 将导出的公钥与支付宝开放平台中 AppID 2021002191650348 配置的应用公钥进行比较。
  3. 确认配置修改已经生效。
  4. 检查部署环境是否仍在读取旧私钥、测试私钥或其他应用的配置。

不要在日志、接口响应或聊天记录中输出完整私钥。

核对支付宝开放平台配置

登录支付宝开放平台,找到 AppID 2021002191650348 对应的应用,逐项确认:

  • 应用处于可用状态,而不是只创建了应用,却没有完成必要配置。
  • 应用已经添加并开通 App 支付能力。
  • 签约主体和相关产品的状态正常。
  • 服务端使用的 AppID 与后台当前应用一致。
  • 应用公钥或应用公钥证书与服务端私钥匹配。
  • 正式环境使用正式应用配置,沙箱环境使用沙箱应用、沙箱网关和对应密钥。
  • 如果是第三方应用代商家调用,是否还需要正确的授权关系和 app_auth_token。

不要仅凭“后台应该配好了”来判断。应直接查看应用能力、签约状态和密钥配置。

检查订单参数

生成的订单字符串通常应包含以下字段:

app_id
method=alipay.trade.app.pay
charset=utf-8
sign_type=RSA2
timestamp
version=1.0
biz_content
sign

对于 biz_content,至少要检查这些内容:

{
  "subject": "订单标题",
  "out_trade_no": "202609090001",
  "total_amount": "0.01",
  "product_code": "QUICK_MSECURITY_PAY"
}

同时注意以下限制:

  • out_trade_no 应由商户生成,并在商户侧保持唯一。
  • total_amount 必须是合法的十进制金额字符串,不能为负数,也不能包含货币符号。
  • 不要使用 1E-8 之类的科学计数法。
  • subject 不能为空。
  • App 支付的 product_code 应为 QUICK_MSECURITY_PAY。Easy SDK 的标准 App 支付接口通常会自动处理,但仍应检查最终订单字符串。
  • 服务器时间应当准确,避免时间戳明显异常。
  • 请求字符集、字符串实际编码和签名所用字符集必须保持一致。

金额最好直接通过字符串构造,避免浮点数精度问题:

String totalAmount = "0.01";

AlipayTradeAppPayResponse response =
        Factory.Payment.App().pay(
                "测试订单",
                "ORDER_" + System.currentTimeMillis(),
                totalAmount
        );

推荐的定位顺序

按以下顺序排查通常更快:

  1. 保存服务端生成的 response.getBody()。
  2. 保存客户端调用 payV2() 前使用的实际字符串。
  3. 比较两段内容是否完全一致,先排除二次编码和 JSON 使用错误。
  4. 检查订单字符串中的 app_id、method、sign_type 和 biz_content。
  5. 验证当前私钥是否与该 AppID 后台配置的应用公钥配对。
  6. 确认当前使用的是普通公钥模式还是证书模式。
  7. 核对 App 支付能力、签约状态,以及正式环境和沙箱环境是否对应。
  8. 查看支付宝 SDK 返回的完整结果,包括 resultStatus、memo 和 result,不要只记录页面上的提示。

如果订单字符串从服务端传到客户端后没有发生变化,订单字段也都合法,接下来应重点检查 AppID、应用私钥、后台应用公钥和 App 支付签约状态之间的配置关系。由于 Payment.App().pay() 不会向支付宝发起请求,这类配置问题通常要等到客户端真正唤起支付时才会暴露。

备注:内容仅供参考。