PAYATHON 2026

H5 页面支付宝支付应调用手机网站支付还是当面付接口?

支付小周

结论

这个流程应使用支付宝手机网站支付,对应接口通常是:

alipay.trade.wap.pay

用户扫描的“第三方二维码”只是进入订单详情页的方式,实际付款发生在 H5 订单页。因此,选择支付产品时,应看用户点击支付按钮时所处的页面形态,不能仅凭流程中是否有二维码来判断。

当面付主要用于线下面对面收款。例如,商家展示支付宝付款二维码,用户扫码后直接进入支付宝收银台付款。其预下单接口通常是:

alipay.trade.precreate

如果用户扫码后先进入商户自己的订单详情页,再点击支付按钮,就不是典型的当面付扫码支付流程。

推荐流程

可以按以下步骤设计:

  1. 商户后台创建业务订单,并生成唯一的 out_trade_no。

  2. 后台生成订单详情页地址,例如:

    https://example.com/orders/202609090001
    
  3. 将该地址生成普通二维码。

  4. 用户使用支付宝扫码,打开 H5 订单详情页。

  5. 用户点击“支付宝支付”。

  6. H5 请求商户后端的支付接口。

  7. 商户后端调用 alipay.trade.wap.pay,生成支付宝收银台表单或跳转地址。

  8. 前端跳转到支付宝收银台,由用户完成付款。

  9. 支付宝通过 notify_url 异步通知商户支付结果。

  10. 商户验签成功后更新订单状态。

这里需要区分两种二维码:

  • 页面入口二维码:二维码内容是商户订单详情页的 URL,由商户自行生成。
  • 支付宝当面付二维码:二维码内容由 alipay.trade.precreate 返回,用户扫码后直接付款。

题述场景使用的是页面入口二维码。

后端调用示例

下面使用支付宝 Java SDK 演示核心调用逻辑。SDK 版本、网关地址以及证书或密钥配置,应以项目的实际接入方式为准。

AlipayClient alipayClient = new DefaultAlipayClient(
    "https://openapi.alipay.com/gateway.do",
    appId,
    merchantPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

request.setReturnUrl("https://example.com/pay/result");
request.setNotifyUrl("https://api.example.com/alipay/notify");

AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo("202609090001");
model.setTotalAmount("99.00");
model.setSubject("订单 202609090001");
model.setProductCode("QUICK_WAP_WAY");

request.setBizModel(model);

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);

if (response.isSuccess()) {
    // body 通常是用于跳转至支付宝收银台的 HTML 表单
    String paymentForm = response.getBody();
    // 将 paymentForm 返回给浏览器
} else {
    throw new IllegalStateException(
        "支付宝下单失败:" + response.getSubCode() + " " + response.getSubMsg()
    );
}

如果接口返回自动提交的 HTML 表单,后端可以直接输出,也可以将其返回给前端,由前端写入页面。具体实现取决于使用的 SDK 和后端框架。

支付按钮处理示例

用户点击前端按钮后,请求商户后端创建支付:

<button id="alipay-button">支付宝支付</button>

<script>
document.getElementById("alipay-button").addEventListener("click", async () => {
  const response = await fetch("/api/orders/202609090001/alipay", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    }
  });

  if (!response.ok) {
    alert("发起支付失败,请稍后重试");
    return;
  }

  const html = await response.text();

  document.open();
  document.write(html);
  document.close();
});
</script>

商户私钥不能保存在浏览器端,也不能由浏览器端使用。签名、金额校验和支付宝接口调用都必须在商户后端完成。

为什么不建议使用当面付

当面付的典型流程是:

商户调用 alipay.trade.precreate
        ↓
支付宝返回支付二维码内容
        ↓
用户使用支付宝扫码
        ↓
直接进入支付流程

题述流程则是:

商户生成订单页面二维码
        ↓
用户扫码进入订单详情页
        ↓
用户主动点击支付
        ↓
跳转支付宝收银台

两种流程的区别不是“是否扫码”,而是二维码中承载的内容,以及付款动作的发起方式。

如果流程改为扫码后直接付款,不再经过订单详情页上的支付按钮,可以考虑使用当面付接口 alipay.trade.precreate。

注意事项

以异步通知为支付成功依据

return_url 只用于支付完成后的页面跳转,不能单独作为订单支付成功的依据。用户可能在跳转前关闭页面,跳转参数也可能被伪造。

商户应通过 notify_url 接收支付宝的异步通知,并完成以下检查:

  • 验证支付宝签名。
  • 校验 app_id。
  • 校验 out_trade_no。
  • 校验订单金额。
  • 校验收款方信息。
  • 判断 trade_status。
  • 对通知进行幂等处理。

不要信任前端提交的金额

前端只需提交订单标识。后端应重新读取订单数据,并据此确定和校验实际支付金额:

Order order = orderService.findByOrderNo(orderNo);

if (order == null) {
    throw new IllegalArgumentException("订单不存在");
}

if (!order.isPendingPayment()) {
    throw new IllegalStateException("订单状态不允许支付");
}

String totalAmount = order.getPayableAmount().setScale(2).toPlainString();

避免为同一订单重复创建支付

用户可能连续点击支付按钮。商户后端应使用唯一且稳定的 out_trade_no,并在发起支付前检查订单状态,避免为同一笔交易生成多笔互不关联的业务订单。

在实际扫码环境中验证

用户使用支付宝扫码后,订单详情页通常会在支付宝内置浏览器中打开。手机网站支付能否正常唤起收银台,还会受到商户产品签约状态、应用配置、域名、页面跳转方式和支付宝客户端环境的影响。

上线前至少需要验证:

  • 支付产品是否已经签约并生效。
  • 应用公钥或证书配置是否正确。
  • notify_url 是否能通过公网 HTTPS 访问。
  • 支付域名和跳转地址是否符合当前开放平台要求。
  • 支付流程在支付宝内置浏览器和普通手机浏览器中是否正常。
  • 用户取消支付、重复支付或支付后关闭页面时,订单状态是否正确。

只要二维码用于打开商户的 H5 订单详情页,并由用户在页面中点击支付,就应优先使用手机网站支付 alipay.trade.wap.pay。只有二维码本身是支付宝付款码,用户扫码后直接付款时,才适合使用当面付。

备注:内容仅供参考。