PC统一收单下单并支付页面接口何时生成支付宝交易号
明确结论
alipay.trade.page.pay(统一收单下单并支付页面接口)的调用结果不会直接返回支付宝交易号 trade_no。
该接口返回的是跳转到支付宝收银台所需的 HTML 表单。此时用户还没有完成付款,商户系统应使用自己的订单号 out_trade_no 跟踪订单。
买家完成支付后,可通过以下方式获取 trade_no:
- 接收支付宝发送到
notify_url的异步通知,从通知参数中读取trade_no。 - 调用
alipay.trade.query主动查询交易。 return_url的同步跳转参数中也可能包含trade_no,但不能据此确认支付结果。
为什么下单接口不直接返回 trade_no
alipay.trade.page.pay 用于生成支付页面,并不会在调用接口时同步完成付款。通常的处理流程如下:
商户创建订单
↓
调用 alipay.trade.page.pay
↓
获得并输出 HTML 表单
↓
用户进入支付宝收银台
↓
用户完成支付
↓
支付宝异步通知 notify_url
↓
商户验签、核对订单并保存 trade_no
调用页面支付接口时,商户能够确定的是自己生成的订单号 out_trade_no。支付宝交易号 trade_no 是支付宝侧的交易标识,商户不能自行生成或推算。
商户系统不需要依赖支付宝内部生成 trade_no 的具体时间点。处理业务时,以支付后的异步通知或交易查询结果中返回的 trade_no 为准。
推荐获取方式:处理异步通知
创建支付请求时,需要设置一个可从公网访问的 notify_url:
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl("https://www.example.com/alipay/notify");
request.setReturnUrl("https://www.example.com/alipay/return");
request.setBizContent("""
{
"out_trade_no": "ORDER_202609090001",
"total_amount": "99.00",
"subject": "测试商品",
"product_code": "FAST_INSTANT_TRADE_PAY"
}
""");
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
String form = response.getBody();
这里的 form 是支付页面表单,不是包含 trade_no 的普通 JSON 数据。商户需要将表单输出到浏览器,再由浏览器提交表单并跳转至支付宝收银台。
支付完成后,异步通知通常包含以下参数:
out_trade_no=ORDER_202609090001
trade_no=20260909220014XXXXXXXXXX
trade_status=TRADE_SUCCESS
total_amount=99.00
服务端收到通知后,应先验签,然后读取并保存 trade_no:
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((key, values) -> {
if (values != null && values.length > 0) {
params.put(key, String.join(",", values));
}
});
boolean valid = AlipaySignature.rsaCheckV1(
params,
ALIPAY_PUBLIC_KEY,
"UTF-8",
"RSA2"
);
if (!valid) {
response.getWriter().write("failure");
return;
}
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 核对 app_id、out_trade_no、total_amount、seller_id 等关键字段。
// 使用 out_trade_no 幂等更新订单,并保存 tradeNo。
}
response.getWriter().write("success");
实际开发时,需要根据所用支付宝 SDK 的版本调整类名和方法签名。
未收到通知时主动查询
如果异步通知出现延迟,或者商户服务暂时不可用,可使用商户订单号调用 alipay.trade.query:
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("""
{
"out_trade_no": "ORDER_202609090001"
}
""");
AlipayTradeQueryResponse queryResponse = alipayClient.execute(request);
if (queryResponse.isSuccess()) {
String tradeNo = queryResponse.getTradeNo();
String tradeStatus = queryResponse.getTradeStatus();
// 核对金额和商户身份后,幂等更新本地订单。
}
查询接口支持传入 out_trade_no。因此,即使本地还没有 trade_no,商户也可以查询支付宝侧的交易状态。支付成功后,查询结果会返回支付宝交易号。
如果交易尚未建立、订单号不存在,或者支付状态还没有更新,查询接口可能暂时无法返回有效的 trade_no。遇到这种情况,可根据业务需要进行有限次数的重试,避免高频轮询。
return_url 为什么不能作为支付依据
用户付款后,浏览器可能跳转到 return_url,同步参数中也可能包含 trade_no。但浏览器跳转并不可靠,常见情况包括:
- 用户付款后直接关闭页面。
- 网络中断,跳转没有完成。
- 浏览器重复刷新或重复访问。
- 请求参数可能被伪造,因此必须验证签名。
return_url 适合展示“支付处理中”或支付结果页面,也可以用来触发一次订单查询,但不适合直接将订单修改为已支付。各接口的用途如下:
return_url:改善用户体验,展示结果或触发一次订单查询
notify_url:服务端确认支付并更新订单
alipay.trade.query:通知缺失时进行主动补偿
注意事项
out_trade_no是商户生成的订单号,trade_no是支付宝生成的交易号,两者不能混用。- 收到异步通知后,必须验证签名,并核对
app_id、seller_id、订单号、金额和交易状态。 - 支付宝可能重复发送异步通知,因此订单更新必须以
out_trade_no为依据,并做好幂等处理。 - 不要只凭前端提示、同步跳转或客户端提交的
trade_no判断支付成功。 - 本地订单表中建议同时保存
out_trade_no和trade_no,便于后续处理退款、撤销、对账和交易查询。 - 向支付宝返回
success前,应确认关键业务处理已经成功。如果处理失败,不要返回虚假的成功结果。
备注:内容仅供参考。
