PAYATHON 2026

支付宝小程序webview中的H5应调用哪种支付API?

支付阿杰

结论

这种场景应接入“支付宝小程序支付”,不建议在小程序的 web-view 中直接调用手机网站支付。

建议采用以下流程:

  1. H5 请求商户服务端创建订单。
  2. 服务端调用支付宝开放平台的 alipay.trade.create,获取 trade_no。
  3. H5 将支付请求发送给小程序页面。
  4. 小程序页面调用 my.tradePay 唤起收银台。
  5. 服务端通过支付宝异步通知或主动查单,确认最终支付结果。

手机网站支付主要用于普通移动浏览器。支付过程依赖页面跳转、拉起支付宝客户端以及支付完成后的 URL 回跳。这些操作在小程序 web-view 的受限容器中可能被拦截,也可能无法正常返回,因此不适合作为首选方案。

推荐的调用链路

web-view 中的 H5
        ↓ 创建业务订单
商户服务端
        ↓ 调用 alipay.trade.create
支付宝服务端
        ↓ 返回 trade_no
H5 / 小程序页面
        ↓ 将 trade_no 交给小程序
小程序调用 my.tradePay
        ↓
支付宝收银台

这里涉及两个不同的 API:

  • alipay.trade.create:由商户服务端调用,用来创建支付宝交易。
  • my.tradePay:由支付宝小程序客户端调用,用来唤起支付收银台。

支付宝应用私钥不能保存在 H5 前端,也不能让 H5 自行拼装或签名支付宝服务端请求。

实现步骤

1. H5 请求商户服务端创建订单

async function createOrder() {
  const response = await fetch('/api/orders', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    credentials: 'include',
    body: JSON.stringify({
      productId: 'PRODUCT_001'
    })
  });

  if (!response.ok) {
    throw new Error('创建订单失败');
  }

  return response.json();
}

金额、商品名称和商户订单号应由服务端根据业务数据生成,不能直接使用前端提交的金额。

2. 服务端调用 alipay.trade.create

下面的代码只展示关键逻辑。SDK 初始化、密钥配置和异常处理,需要结合实际使用的服务端技术栈补充:

const result = await alipaySdk.exec('alipay.trade.create', {
  bizContent: {
    out_trade_no: order.orderNo,
    total_amount: order.amount,
    subject: order.subject,
    buyer_id: buyerId
  },
  notify_url: 'https://example.com/api/alipay/notify'
});

const tradeNO = result.tradeNo;

不同语言和版本的支付宝 SDK,字段名称可能有所不同,具体应以对应 SDK 的返回结构为准。交易创建成功后,服务端将 trade_no 返回给前端。

3. H5 通知小程序发起支付

H5 可以通过支付宝小程序 WebView Bridge 向宿主小程序发送消息:

<script src="https://appx/web-view.min.js"></script>
<script>
  async function pay() {
    try {
      const order = await createOrder();

      my.postMessage({
        type: 'PAY',
        orderNo: order.orderNo,
        tradeNO: order.tradeNO
      });
    } catch (error) {
      console.error(error);
    }
  }
</script>

WebView Bridge 的引入方式和可用能力可能随运行环境变化,应以当前支付宝小程序文档和实际基础库版本为准。

4. 小程序页面调用 my.tradePay

小程序页面收到 H5 消息后,在小程序环境中唤起支付:

<web-view
  src="{{h5Url}}"
  onMessage="handleWebViewMessage"
/>
Page({
  handleWebViewMessage(event) {
    // 消息数据的具体层级需根据当前基础库实际事件结构确认
    const message =
      event.detail && event.detail.data
        ? event.detail.data
        : event.detail;

    if (!message || message.type !== 'PAY') {
      return;
    }

    my.tradePay({
      tradeNO: message.tradeNO,
      success: (result) => {
        this.queryOrderStatus(message.orderNo);
      },
      fail: (error) => {
        console.error('支付未完成', error);
      }
    });
  },

  queryOrderStatus(orderNo) {
    // 向商户服务端查询订单状态
  }
});

更稳妥的方式是让 H5 只传递商户订单号 orderNo,再由小程序向服务端换取对应的 trade_no,以降低支付参数被伪造的风险。

支付结果处理

my.tradePay 的成功回调只表示客户端支付流程返回成功,不能作为发货、充值或开通服务的唯一依据。

服务端还需要完成以下检查:

  • 验证支付宝异步通知的签名。
  • 校验 app_id、商户订单号、金额和收款方信息。
  • 确认交易状态符合业务要求。
  • 对同一通知进行幂等处理。
  • 必要时调用支付宝交易查询接口,再次确认交易状态。

前端完成支付后,可以轮询商户服务端的订单查询接口:

async function waitForPaid(orderNo) {
  const response = await fetch(
    `/api/orders/${encodeURIComponent(orderNo)}`
  );
  const order = await response.json();

  if (order.status === 'PAID') {
    // 展示支付成功页面
  }
}

注意事项

  • web-view 中的页面域名需要加入小程序业务域名或相关白名单。
  • 支付宝应用、商户账号和小程序需要完成相应的签约、绑定和支付能力配置。
  • trade_no 应由商户服务端创建,私钥不能放在 H5 或小程序代码中。
  • 不要直接信任 H5 传入的金额、订单归属或支付状态。
  • 手机网站支付适用于普通移动网页。即使在某些情况下能够从 web-view 中拉起,也可能遇到跳转、回跳和兼容性问题。
  • 如果当前小程序账号尚未开通相应的支付能力,仅将接口改为 my.tradePay 仍然无法完成支付。需要先检查产品签约、应用绑定和权限配置。

需要调整的并不是让 H5 改调另一个网页支付接口,而是让 H5 负责业务展示和下单,再把收银台调用交给支付宝小程序,由小程序通过 my.tradePay 完成支付。

备注:内容仅供参考。