服务器创建支付宝支付订单后如何获取客户端orderinfo
结论
使用支付宝 App 支付接口 alipay.trade.app.pay 时,服务端应调用 SDK 的 sdkExecute() 生成订单字符串:
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
String orderInfo = response.getBody();
response.getBody() 就是客户端调用 payV2(orderInfo, true) 时需要的 orderInfo。服务端应将这段字符串原样返回,不要解析 JSON、重新拼接、URL 解码或再次签名。
如果返回内容是 JSON 格式的接口响应,而不是由 & 连接的完整参数字符串,通常是因为调用了 execute(),或者使用了其他支付接口。这类 JSON 响应不能直接作为 App 支付的 orderInfo。
正确的服务端处理方式
以 Java SDK 为例,App 支付订单可以这样生成:
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2"
);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
// 支付宝服务器异步通知地址,必须是公网可访问的 HTTPS/HTTP 地址
request.setNotifyUrl("https://example.com/alipay/notify");
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setTotalAmount("99.00");
model.setSubject("商品名称");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
String orderInfo = response.getBody();
// 将 orderInfo 原样返回给客户端
return orderInfo;
orderInfo 通常是一串由 & 连接的参数,例如:
app_id=...&biz_content=...&charset=UTF-8&method=alipay.trade.app.pay&sign_type=RSA2×tamp=...&version=1.0&sign=...
字段的实际顺序和编码形式由 SDK 决定,业务代码不应依赖固定的排列顺序。
sdkExecute() 和 execute() 的区别
这两个方法很容易混淆。
sdkExecute()
sdkExecute() 用于生成客户端 SDK 发起支付所需的完整订单字符串:
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
String orderInfo = response.getBody();
返回的 body 已经包含公共参数、业务参数和签名,可以直接交给客户端。
execute()
AlipayTradeAppPayResponse response = alipayClient.execute(request);
execute() 会向支付宝网关发起服务端接口请求,并解析支付宝返回的响应。它适合由服务端直接调用的接口,但不适合生成 App 支付所需的 orderInfo。
如果当前拿到的是下面这种 JSON:
{
"alipay_trade_app_pay_response": {
"code": "...",
"msg": "..."
},
"sign": "..."
}
不要自行将它转换成 orderInfo。应先检查是否误用了 execute(),然后改为调用 sdkExecute()。
客户端如何使用
Android 客户端收到服务端返回的订单字符串后,可以直接调用:
PayTask payTask = new PayTask(activity);
Map<String, String> result = payTask.payV2(orderInfo, true);
服务端可以用 JSON 包装订单字符串,让业务接口的返回结构更清晰:
{
"code": 0,
"data": {
"orderInfo": "app_id=...&biz_content=...&sign=..."
}
}
客户端取出 data.orderInfo 即可。这里的 JSON 只是业务接口的外层包装,orderInfo 字段内部仍须保留支付宝 SDK 生成的原始字符串。
RSA2 与公钥证书模式不是同一个维度
RSA2 和公钥证书模式并不是两种只能二选一的签名算法。
RSA2是签名算法类型,通常表示SHA256WithRSA。- 普通公钥模式和公钥证书模式,是支付宝及应用公钥的配置、识别和管理方式。
- 使用公钥证书模式时,签名算法一般仍配置为
RSA2。
因此,公钥证书模式可以与 RSA2 同时使用。
普通公钥模式
普通公钥模式通常需要配置:
- 应用私钥;
- 支付宝公钥;
signType = RSA2。
示例:
AlipayClient alipayClient = new DefaultAlipayClient(
GATEWAY_URL,
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2"
);
这里必须填写支付宝公钥,不能误填应用公钥,也不能直接填入支付宝公钥证书的完整内容。
公钥证书模式
证书模式通常需要配置:
- 应用私钥;
- 应用公钥证书;
- 支付宝公钥证书;
- 支付宝根证书;
signType = RSA2。
不同版本的 Java SDK 可能使用不同的配置类和构造方式,常见写法如下:
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do");
certAlipayRequest.setAppId(APP_ID);
certAlipayRequest.setPrivateKey(APP_PRIVATE_KEY);
certAlipayRequest.setFormat("json");
certAlipayRequest.setCharset("UTF-8");
certAlipayRequest.setSignType("RSA2");
certAlipayRequest.setCertPath("/path/to/appCertPublicKey.crt");
certAlipayRequest.setAlipayPublicCertPath("/path/to/alipayCertPublicKey_RSA2.crt");
certAlipayRequest.setRootCertPath("/path/to/alipayRootCert.crt");
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
具体类名和证书参数形式要以项目实际使用的支付宝 Java SDK 版本为准。有些版本支持传入证书文件路径,有些封装则可能接收证书内容。
如何选择配置
配置方式取决于支付宝开放平台中当前应用使用的密钥模式:
- 如果应用配置的是普通公钥模式,应使用应用私钥和支付宝公钥初始化客户端。
- 如果应用配置的是公钥证书模式,应使用三份证书和应用私钥初始化客户端。
- 不要混用普通公钥模式的
AlipayClient配置和证书模式的文件。 - 无论采用哪种公钥配置模式,
signType通常都应与开放平台的配置一致。当前使用 RSA2 时,应填写"RSA2"。
支付下单、同步结果解析和异步通知验签,都应使用同一应用对应的配置模式。
异步通知验签
客户端返回的支付结果只适合用于界面提示,不能作为服务端确认支付成功的唯一依据。服务端应接收支付宝异步通知,完成验签,并至少核对以下信息:
app_id是否属于当前应用;out_trade_no是否存在,且没有被错误处理;total_amount是否与订单金额一致;seller_id是否属于预期收款账号;trade_status是否为业务接受的成功状态。
普通公钥模式应使用支付宝公钥验签。证书模式应使用对应 SDK 提供的证书验签方法或证书配置。不要使用应用公钥验证支付宝返回的签名。
常见问题
-
服务端再次对
response.getBody()签名不需要再次签名。
sdkExecute()已经完成签名,重复签名会导致客户端验签失败。 -
解析后重新拼接
orderInfo不建议这样处理。字段顺序、字符编码、转义或 URL 编码一旦发生变化,就可能破坏签名。
-
把
response对象序列化后返回客户端需要的是
response.getBody(),不是完整的 Java 响应对象。 -
日志或接口框架修改了字符串
需要确认处理过程中没有发生 HTML 转义、重复 URL 编码、插入换行,或将
+号替换为空格等情况。 -
沙箱与正式环境配置混用
网关地址、应用 ID、密钥和证书必须属于同一环境、同一应用。沙箱配置不能直接用于正式环境。
备注:内容仅供参考。
