PAYATHON 2026

鸿蒙应用内嵌H5如何唤起支付宝支付

支付老李

结论

在鸿蒙应用的内嵌 H5 中唤起支付宝,并不能通过 Web 组件的一项配置直接实现。具体接入方式取决于所使用的支付产品:

  • 如果使用“手机网站支付”,服务端调用 alipay.trade.wap.pay,H5 再提交支付宝返回的支付表单。Web 组件还要正确处理支付宝页面的跳转以及支付后的回跳。
  • 如果使用“App 支付”,H5 通过 JSBridge 通知鸿蒙原生层,由原生层调用支付宝为 HarmonyOS 提供的支付 SDK。支付订单必须在服务端签名,应用私钥不能保存在 H5 或鸿蒙客户端中。

对于应用内嵌页面,通常更适合采用第二种方式,因为支付流程和结果回传更容易控制。不过,当前接入的支付宝产品必须提供适用于目标 HarmonyOS 版本的客户端 SDK,具体支持情况应以接入时的支付宝开放平台文档和 SDK 为准。

为什么不能直接在 H5 中调用 App 支付

支付宝 App 支付需要客户端 SDK 配合。普通 H5 页面无法安全保存商户私钥,也不能直接调用鸿蒙原生 SDK。

即使 H5 获得了类似 alipays:// 的跳转地址,Web 组件也不一定会像系统浏览器那样将地址交给支付宝应用处理。实际行为可能受到系统版本、Web 内核和支付宝客户端版本的影响,因此,拼接支付宝 URL Scheme 不能作为稳定的支付方案。

各端可以按以下方式分工:

  1. H5 请求业务服务端创建订单。
  2. 服务端调用支付宝接口并完成签名。
  3. H5 将服务端返回的订单字符串传给鸿蒙原生层。
  4. 原生层调用支付宝 SDK。
  5. 服务端接收支付宝异步通知,确认最终支付状态。
  6. 客户端查询服务端的订单状态并更新页面。

基础权限配置

Web 页面需要访问网络,因此要在模块的 module.json5 中声明网络权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

如果页面、支付接口或回调地址使用 HTTP,还要处理明文流量限制。正式环境应统一使用 HTTPS,不建议为支付场景开放全局明文访问。

推荐方案:H5 通过 JSBridge 调用原生支付

H5 发起支付

H5 先请求自己的服务端创建订单。服务端返回支付宝 SDK 所需的订单字符串后,H5 调用鸿蒙侧注入的 JSBridge:

async function startAlipay(orderId) {
  const response = await fetch('/api/payment/alipay/create', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ orderId })
  });

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

  const data = await response.json();

  if (!window.HarmonyPayBridge) {
    throw new Error('当前环境不支持原生支付宝支付');
  }

  // orderString 由服务端生成并签名
  window.HarmonyPayBridge.pay(data.orderString);
}

window.onAlipayResult = async function (result) {
  // 原生结果只能用于改善交互,不能作为到账依据
  console.log('支付宝客户端返回结果:', result);

  // 重新向业务服务端查询订单状态
  const response = await fetch(
    `/api/payment/status?orderId=${encodeURIComponent(result.orderId)}`
  );
  const payment = await response.json();

  if (payment.status === 'PAID') {
    showPaymentSuccess();
  } else {
    showPaymentPending();
  }
};

支付宝应用私钥、签名逻辑和商户敏感配置都不能放进这段代码。

鸿蒙 Web 组件注入桥接对象

下面是 JSBridge 的基本结构。payWithAlipaySdk 是业务侧的封装方法,需要在其中调用实际接入的支付宝 HarmonyOS SDK,并不是支付宝官方 API 的名称。

import { webview } from '@kit.ArkWeb';

class HarmonyPayBridge {
  private onPayRequest: (orderString: string) => void;

  constructor(onPayRequest: (orderString: string) => void) {
    this.onPayRequest = onPayRequest;
  }

  pay(orderString: string): void {
    if (!orderString) {
      return;
    }

    this.onPayRequest(orderString);
  }
}

@Entry
@Component
struct PaymentPage {
  private controller: webview.WebviewController =
    new webview.WebviewController();

  private bridge: HarmonyPayBridge = new HarmonyPayBridge(
    (orderString: string) => {
      this.startAlipay(orderString);
    }
  );

  private async startAlipay(orderString: string): Promise<void> {
    try {
      // 此处调用实际接入的支付宝 HarmonyOS SDK
      const result = await payWithAlipaySdk(orderString);

      const callbackData = JSON.stringify({
        orderId: result.orderId,
        resultStatus: result.resultStatus
      });

      await this.controller.runJavaScript(
        `window.onAlipayResult(${callbackData})`
      );
    } catch (error) {
      const callbackData = JSON.stringify({
        resultStatus: 'SDK_ERROR'
      });

      await this.controller.runJavaScript(
        `window.onAlipayResult(${callbackData})`
      );
    }
  }

  build() {
    Web({
      src: 'https://example.com/payment',
      controller: this.controller
    })
      .javaScriptAccess(true)
      .javaScriptProxy({
        object: this.bridge,
        name: 'HarmonyPayBridge',
        methodList: ['pay'],
        controller: this.controller
      });
  }
}

这段示例主要说明 H5 与原生层之间的调用关系。支付宝 SDK 的包名、初始化方法、支付方法和返回类型都可能随版本变化,不能把示例中的 payWithAlipaySdk 直接当作官方接入代码使用。

JSBridge 也不能向任意网页开放。至少要检查当前 Web 页面的域名,只允许受信任的业务页面调用支付方法。

服务端生成支付订单

采用 App 支付时,服务端通常调用 alipay.trade.app.pay。下面是 Java 侧的结构示例:

AlipayClient alipayClient = new DefaultAlipayClient(
    gatewayUrl,
    appId,
    privateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(outTradeNo);
model.setSubject(subject);
model.setTotalAmount(totalAmount);
model.setProductCode("QUICK_MSECURITY_PAY");

request.setBizModel(model);
request.setNotifyUrl("https://api.example.com/payment/alipay/notify");

AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

if (!response.isSuccess()) {
    throw new IllegalStateException("支付宝订单创建失败");
}

String orderString = response.getBody();

客户端只接收最终的 orderString。商户私钥必须保存在服务端,并通过密钥管理、访问控制和日志脱敏等措施加以保护。

可选方案:使用手机网站支付

如果商户申请的是手机网站支付,服务端应调用 alipay.trade.wap.pay:

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo(outTradeNo);
model.setSubject(subject);
model.setTotalAmount(totalAmount);
model.setProductCode("QUICK_WAP_WAY");

request.setBizModel(model);
request.setReturnUrl("https://m.example.com/payment/result");
request.setNotifyUrl("https://api.example.com/payment/alipay/notify");

String formHtml = alipayClient.pageExecute(request).getBody();

formHtml 是支付宝返回的提交表单。服务端可以直接将其输出为 HTML 页面,也可以交给受信任的 H5 页面渲染并提交:

<div id="alipay-form"></div>

<script>
  async function submitWapPayment(orderId) {
    const response = await fetch(
      `/api/payment/alipay/wap-form?orderId=${encodeURIComponent(orderId)}`
    );

    const formHtml = await response.text();
    const container = document.getElementById('alipay-form');
    container.innerHTML = formHtml;

    const form = container.querySelector('form');
    if (!form) {
      throw new Error('未获取到支付宝支付表单');
    }

    form.submit();
  }
</script>

支付宝生成的页面之后可能尝试打开支付宝客户端。鸿蒙 Web 组件能否顺利处理这次跳转,需要在目标设备和相应版本的支付宝客户端上实际验证。如果内嵌 Web 的兼容性不理想,可以改用系统浏览器打开 WAP 支付页,再通过 HTTPS App Link 或业务查询接口恢复支付结果页。

支付结果必须由服务端确认

支付宝 SDK 返回成功,或者 H5 跳转到了 return_url,都不表示服务端已经确认收款。支付结果应按以下流程处理:

  1. 服务端接收 notify_url 的异步通知。
  2. 按照支付宝文档验证通知签名。
  3. 校验 app_id、商户身份、订单号、金额和币种等字段。
  4. 通过幂等逻辑更新订单,避免重复通知触发重复处理。
  5. 客户端向业务服务端查询订单状态。

客户端收到的 resultStatus 只能用来展示“正在确认”或“用户取消”等即时状态,不能直接决定发货、充值或开通权益。

注意事项

  • 不要在客户端自行拼接或长期依赖 alipays:// 地址。支付宝生成的跳转参数和兼容策略可能发生调整。
  • 不要把 return_url 当作支付成功凭据。最终状态应以服务端验签后的异步通知或主动查询结果为准。
  • JSBridge 必须限制来源域名,并检查参数长度、格式和当前登录用户。
  • 创建支付订单时,服务端应重新读取商品价格,不能信任 H5 上传的金额。
  • out_trade_no 必须唯一,异步通知处理必须具备幂等性。
  • 回调页面需要处理应用切到后台、支付宝未安装、用户取消、网络中断以及支付结果尚未同步等情况。
  • 上线前至少要测试真机支付、取消支付、重复点击、应用被系统回收、通知重复到达和回跳失败等场景。
  • HarmonyOS 和支付宝 SDK 的接口可能继续更新。最终使用的包依赖、签名配置和调用方法,应以接入时的支付宝开放平台文档为准。

备注:内容仅供参考。