java app支付宝支付交易订单处理失败如何解决?
结论
这组参数有两个明显问题:
productCode使用了FAST_INSTANT_TRADE_PAY。调用alipay.trade.app.pay时,通常应使用QUICK_MSECURITY_PAY。FAST_INSTANT_TRADE_PAY主要用于电脑网站支付,与 App 支付接口不匹配。biz_content使用了outTradeNo、productCode、timeoutExpress、totalAmount等字段名,而支付宝接口接收的 JSON 字段名是out_trade_no、product_code、timeout_express、total_amount。如果 JSON 是手工拼接的,支付宝可能无法正确识别这些参数。
目前只有订单字符串,无法判断签名是否正确。验签还需要参与签名的原始参数串、应用私钥所对应的支付宝公钥,以及实际的编码过程。建议先修正业务参数,再通过支付宝 Java SDK 生成订单字符串,避免手工拼接和 URL 编码。
当前参数中的主要问题
URL 解码后的 biz_content 大致如下:
{
"body": "boy",
"outTradeNo": "20240724102917A001",
"productCode": "FAST_INSTANT_TRADE_PAY",
"subject": "Subject",
"timeoutExpress": "30m",
"totalAmount": "100.00"
}
建议改为:
{
"body": "boy",
"out_trade_no": "20240724102917A001",
"product_code": "QUICK_MSECURITY_PAY",
"subject": "Subject",
"timeout_express": "30m",
"total_amount": "100.00"
}
还需要注意以下几点:
- Java 对象的属性可以命名为
outTradeNo,但最终发送给支付宝的 JSON 字段必须符合接口定义。 - 使用官方 SDK 的
AlipayTradeAppPayModel时,SDK 会处理字段映射。如果使用Map、第三方 JSON 工具或手工拼接字符串,就必须自行使用下划线字段名。 FAST_INSTANT_TRADE_PAY不适用于 App 支付订单。out_trade_no在当前商户范围内必须唯一。重复提交时,需要确认旧订单是否已经创建。
推荐的 Java 实现
不要直接拼接查询字符串,也不要在签名前单独对部分参数进行 URL 编码。可以让支付宝 SDK 完成参数序列化、签名和订单字符串生成:
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
public class AlipayOrderService {
private static final String GATEWAY =
"https://openapi.alipay.com/gateway.do";
public String createOrder() throws AlipayApiException {
String appId = "你的应用 App ID";
String merchantPrivateKey = "你的应用私钥";
String alipayPublicKey = "支付宝公钥";
AlipayClient alipayClient = new DefaultAlipayClient(
GATEWAY,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("20240724102917A001");
model.setProductCode("QUICK_MSECURITY_PAY");
model.setSubject("Subject");
model.setBody("boy");
model.setTotalAmount("100.00");
model.setTimeoutExpress("30m");
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setBizModel(model);
request.setNotifyUrl("https://saas.a1a9.com/api/alipay/notify");
AlipayTradeAppPayResponse response =
alipayClient.sdkExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"生成支付宝订单失败:" +
response.getCode() + " / " +
response.getSubCode() + " / " +
response.getSubMsg()
);
}
return response.getBody();
}
}
response.getBody() 返回的就是应交给支付宝 App SDK 的完整订单字符串。客户端不要调整字段顺序、重新签名,也不要再次对整个字符串进行 URL 编码。
签名排查方法
修正参数后如果仍然失败,可以按以下顺序检查。
1. 确认密钥对应关系
DefaultAlipayClient 中应配置:
- 商户应用私钥,不能使用支付宝私钥或应用公钥;
- 支付宝公钥,不能使用商户应用公钥;
sign_type使用RSA2时,密钥配置也必须与 RSA2 签名方式匹配;- 当前私钥必须与支付宝开放平台中该
app_id配置的应用公钥对应。
如果应用重新上传过公钥,而服务端仍在使用旧私钥,签名一定无法通过。
2. 检查环境是否一致
正式环境通常使用:
https://openapi.alipay.com/gateway.do
沙箱应用必须同时使用沙箱网关、沙箱 App ID 和沙箱密钥,不能与正式环境的配置混用。
3. 不要二次编码订单字符串
签名是根据特定的参数内容计算出来的。签名完成后,以下操作都可能破坏签名:
- 对整个订单字符串再次调用
URLEncoder.encode(); - 解码后重新组装参数;
- 修改
biz_content; - 混用
+、空格和%2B; - 按自定义规则重新排序或过滤参数。
服务端应将 sdkExecute() 返回的字符串原样传给 App。
4. 检查服务器时间
示例中的 timestamp=2024-07-24 10:29:18 已经过期,不能继续用这条历史订单字符串发起支付。每次创建订单时都应实时生成参数和签名,并确保服务器时间准确。
回调地址检查
当前 notify_url 使用的是 HTTP:
http://saas.a1a9.com/api/alipay/notify
这通常不会直接引发“交易订单处理失败”,但生产环境建议改用公网可访问的 HTTPS 地址。回调接口还应满足以下要求:
- 支付宝服务器可以直接访问;
- 不要求登录,也不依赖 Cookie 或验证码;
- 不会被防火墙、网关鉴权或 CSRF 校验拦截;
- 收到通知后使用支付宝公钥验签;
- 验签成功并完成业务处理后,返回纯文本
success; - 不依赖参数顺序,也不在验签前修改参数值。
回调验签失败不会让用户在创建订单时立即看到报错,但会导致支付完成后无法正确更新业务订单。
获取真正的失败原因
“交易订单处理失败”只是客户端显示的概括性提示,单凭这句话无法判断问题出在签名、权限还是业务参数。服务端应完整记录以下字段:
response.getCode();
response.getMsg();
response.getSubCode();
response.getSubMsg();
response.getBody();
排查时重点查看 sub_code 和 sub_msg。常见原因包括:
- 应用未开通 App 支付能力;
- 应用、商户账号或签约产品不匹配;
app_id与密钥不匹配;product_code错误;out_trade_no重复;- 金额或必填字段格式错误;
- 签名前后参数被修改;
- 正式环境与沙箱配置混用。
建议先将 product_code 改为 QUICK_MSECURITY_PAY,使用 AlipayTradeAppPayModel 和 sdkExecute() 重新生成订单字符串,再根据支付宝返回的 sub_code 排查其余问题。
备注:内容仅供参考。
