easy-SDKPayment.App().pay 支付提示商家订单参数异常
结论
Payment.App().pay() 只负责在服务端本地组装并签名 App 支付订单,不会立即向支付宝服务器发起请求。因此,这个接口没有报错,不代表订单参数、签名和应用配置一定正确。
出现“商家订单参数异常,请重新发起付款”时,通常需要检查以下问题:
- 客户端没有原样使用
response.getBody(),对内容进行了二次 URL 编码、解码或 JSON 转义。 app_id、签名私钥和支付宝后台应用不匹配。- 应用没有开通 App 支付能力,或者使用了错误的环境或应用。
- 签名模式配置有误,例如混用了普通公钥模式和证书模式。
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,支付宝客户端在校验订单时仍会拒绝支付。
可以按以下步骤重新验证密钥关系:
- 从服务端当前使用的应用私钥导出公钥。
- 将导出的公钥与支付宝开放平台中 AppID
2021002191650348配置的应用公钥进行比较。 - 确认配置修改已经生效。
- 检查部署环境是否仍在读取旧私钥、测试私钥或其他应用的配置。
不要在日志、接口响应或聊天记录中输出完整私钥。
核对支付宝开放平台配置
登录支付宝开放平台,找到 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
);
推荐的定位顺序
按以下顺序排查通常更快:
- 保存服务端生成的
response.getBody()。 - 保存客户端调用
payV2()前使用的实际字符串。 - 比较两段内容是否完全一致,先排除二次编码和 JSON 使用错误。
- 检查订单字符串中的
app_id、method、sign_type和biz_content。 - 验证当前私钥是否与该 AppID 后台配置的应用公钥配对。
- 确认当前使用的是普通公钥模式还是证书模式。
- 核对 App 支付能力、签约状态,以及正式环境和沙箱环境是否对应。
- 查看支付宝 SDK 返回的完整结果,包括
resultStatus、memo和result,不要只记录页面上的提示。
如果订单字符串从服务端传到客户端后没有发生变化,订单字段也都合法,接下来应重点检查 AppID、应用私钥、后台应用公钥和 App 支付签约状态之间的配置关系。由于 Payment.App().pay() 不会向支付宝发起请求,这类配置问题通常要等到客户端真正唤起支付时才会暴露。
备注:内容仅供参考。
