PAYATHON 2026

支付宝手机网站支付如何只下单并获取支付宝订单号?

支付老李

结论

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

手机网站支付通常包含以下步骤:

  1. 商户生成唯一的 out_trade_no。
  2. 商户服务端调用支付宝 SDK,生成自动提交的 HTML 表单。
  3. App 或手机浏览器加载表单,请求支付宝收银台。
  4. 支付宝受理请求并创建交易。
  5. 用户完成支付,或暂时保持未支付状态。
  6. 商户通过异步通知或 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 无法实现”只下单并立即返回支付宝订单号”。

备注:内容仅供参考。