支付宝手机网站支付如何只下单并获取支付宝订单号?
结论
alipay.trade.wap.pay 不支持”只创建交易并直接返回支付宝订单号”。
这个接口返回的是支付宝收银台的 HTML 表单。服务端调用 SDK 时只生成表单,并未向支付宝发起创建交易的请求。此时通常只有商户订单号 out_trade_no,还没有支付宝交易号 trade_no。
如果可以接受用户打开一次支付宝收银台但不付款,可以在收银台拉起后,用 out_trade_no 调用 alipay.trade.query。支付宝创建交易后,查询结果会包含 trade_no,交易状态通常是 WAIT_BUYER_PAY。
如果收银台不能打开,用户也不能进行任何操作,手机网站支付链路就无法保证提前取得 trade_no。这种情况可以评估 alipay.trade.create 等交易创建接口,但这些接口有各自的准入要求、买家身份要求和适用条件,不能默认将其作为 alipay.trade.wap.pay 的前置接口。
为什么生成支付表单时拿不到 trade_no
手机网站支付通常包含以下步骤:
- 商户生成唯一的
out_trade_no。 - 商户服务端调用支付宝 SDK,生成自动提交的 HTML 表单。
- App 或手机浏览器加载表单,请求支付宝收银台。
- 支付宝受理请求并创建交易。
- 用户完成支付,或暂时保持未支付状态。
- 商户通过异步通知或
alipay.trade.query获取交易结果。
最容易混淆的是第 2 步。下面的调用:
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
String form = response.getBody();
返回的是等待提交的 HTML,并非支付宝网关返回的交易创建结果。此时支付宝侧可能还没有对应的交易,也就没有可供查询的 trade_no。
两个订单号的含义并不相同:
out_trade_no:商户订单号,由商户系统创建并保证唯一。trade_no:支付宝交易号,由支付宝在创建交易后分配。
可行的实现流程
如果业务可以接受”拉起收银台,但用户不一定付款”,可以按下面的流程实现。
1. 服务端创建商户订单
先生成唯一且不能重复使用的 out_trade_no,并在数据库中保存订单金额、状态和创建时间。
String outTradeNo = generateUniqueOrderNo();
不要使用 trade_no 作为本地订单的主键,因为向支付宝发起请求之前,这个编号还不存在。
2. 生成手机网站支付表单
AlipayClient alipayClient = new DefaultAlipayClient(
gatewayUrl,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setNotifyUrl(notifyUrl);
request.setReturnUrl(returnUrl);
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo(outTradeNo);
model.setTotalAmount("0.01");
model.setSubject("测试订单");
model.setProductCode("QUICK_WAP_WAY");
model.setQuitUrl(quitUrl);
request.setBizModel(model);
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
String form = response.getBody();
将 form 返回给 App,再由 WebView 或系统浏览器加载。支付宝客户端能否拉起,取决于支付宝提供的手机网站转客户端流程、终端环境以及相关配置。
只执行 pageExecute(),并不代表支付宝交易已经创建。
3. 收银台打开后查询交易
客户端可以通知服务端开始查询,服务端也可以按一定间隔进行有限次数的轮询。
AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
AlipayTradeQueryModel queryModel = new AlipayTradeQueryModel();
queryModel.setOutTradeNo(outTradeNo);
queryRequest.setBizModel(queryModel);
AlipayTradeQueryResponse queryResponse =
alipayClient.execute(queryRequest);
if (queryResponse.isSuccess()) {
String tradeNo = queryResponse.getTradeNo();
String tradeStatus = queryResponse.getTradeStatus();
// 保存 tradeNo,并根据 tradeStatus 更新本地订单状态
}
查询可能得到以下结果:
- 查询成功并返回
trade_no:支付宝已经创建交易。 trade_status为WAIT_BUYER_PAY:交易已经创建,但用户尚未付款。- 查询不到交易:请求可能尚未到达支付宝,收银台可能没有真正打开,也可能是交易仍在创建中。
因此,查询不到交易时不要立即认定支付失败。可以在短时间内重试,但必须限制重试次数和频率。
是否可以使用 alipay.trade.create
从接口用途来看,alipay.trade.create 更接近”由服务端创建支付宝交易”,成功响应中可以包含支付宝交易号。不过,能否用于当前业务,还要看以下条件:
- 当前应用是否拥有该接口所需的产品权限;
- 是否具有接口要求的买家身份信息,例如
buyer_id; - 创建的交易能否与后续支付产品正确衔接;
- 支付宝当前的产品规则是否允许这种组合方式。
不能先调用 alipay.trade.create,再直接使用同一个 out_trade_no 调用 alipay.trade.wap.pay。支付宝通常要求商户订单号唯一,重复创建可能导致订单已存在、参数不一致或产品不匹配等错误。
如果目标是让用户在 App 内直接调用支付宝客户端付款,通常应申请并接入支付宝 App 支付能力。手机网站转客户端仍属于手机网站支付链路,不能视为原生 App 支付接口。
注意事项
不要把同步跳转当作最终支付结果
return_url 主要用于支付完成后的页面跳转。用户可能关闭页面,也可能遇到网络中断或跳转失败。订单状态应以支付宝异步通知为主,并通过 alipay.trade.query 主动核验。
异步通知必须验签
收到通知后,至少要完成以下检查:
- 验证支付宝签名;
- 检查
app_id; - 检查
out_trade_no是否属于本商户; - 检查金额和币种;
- 检查卖家身份;
- 根据
trade_status幂等更新订单。
未支付交易也需要关闭
如果只是测试交易创建,或用户放弃付款,应为本地订单设置有效期。订单超过有效期后,可以根据实际状态调用 alipay.trade.close 关闭未支付交易,避免长期堆积。
不要伪造或自行推导 trade_no
支付宝交易号必须以支付宝接口、异步通知或查询结果返回的值为准。它不能由 out_trade_no 转换得到,也不能由服务端自行生成。
当前场景的答案很明确:商户先生成 out_trade_no。支付请求真正到达支付宝并创建交易后,才能通过 alipay.trade.query、异步通知或支付返回参数取得 trade_no。如果收银台都不打开,仅靠 alipay.trade.wap.pay 无法实现”只下单并立即返回支付宝订单号”。
备注:内容仅供参考。
