h5 唤起支付宝支付时鸿蒙系统出现中间页,如何处理?
结论
鸿蒙系统出现“打开支付宝 / 下载支付宝”的中间页,通常是浏览器或支付宝收银台判断应用唤起能力后采用的正常降级流程。H5 页面无法强制跳过该页面,也不能保证在所有鸿蒙设备和浏览器中直接唤起支付宝。
业务侧应接入支付宝官方的手机网站支付能力,将服务端生成的支付表单或收银台地址交给浏览器处理,不要自行拼接 alipays:// Scheme。当前环境支持应用唤起时,支付宝会尝试打开客户端;环境不支持、客户端未安装或唤起请求被浏览器拦截时,则会显示中间页,或继续使用网页支付。
为什么鸿蒙系统更容易出现中间页
能否直接打开支付宝,不只由 H5 代码决定,还会受到以下因素影响:
- 鸿蒙系统版本及其应用兼容能力
- 支付宝客户端是否安装
- 当前浏览器是否允许网页调用外部应用
- 页面操作是否由用户点击触发
- 浏览器针对 URL Scheme、应用链接和重定向的安全策略
- 支付宝收银台对当前 User-Agent 和运行环境的识别结果
- 页面是否运行在微信、钉钉等应用的内置 WebView 中
相同的实现方式在安卓设备上没有出现中间页,不代表它在鸿蒙系统上也能直接唤起支付宝。中间页通常用于检测应用是否存在、请求用户确认,并在未安装应用时提供下载引导。业务页面没有可靠的接口可以将其关闭。
还要注意区分 HarmonyOS 和 HarmonyOS NEXT。后者不能视为传统的 Android 兼容环境。能否唤起支付宝,需要根据目标设备、浏览器和已安装的支付宝版本进行实际测试。
推荐处理方式
1. 使用支付宝手机网站支付接口
服务端应调用支付宝开放平台提供的手机网站支付接口,例如 alipay.trade.wap.pay。不要在前端保存应用私钥,也不要由前端直接生成签名。
以下代码仅用于展示典型的服务端调用结构。具体类名和配置方式应以当前使用的支付宝服务端 SDK 版本为准:
AlipayClient alipayClient = new DefaultAlipayClient(
gatewayUrl,
appId,
privateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);
request.setBizContent("""
{
"out_trade_no": "ORDER_202609090001",
"total_amount": "99.00",
"subject": "订单支付",
"product_code": "QUICK_WAP_WAY"
}
""");
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
String paymentForm = response.getBody();
订单号、金额和商品信息必须取自服务端的可信数据,不能直接使用前端传入的值。
2. 将支付表单返回给浏览器
服务端可以直接响应支付宝 SDK 生成的 HTML 表单:
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(paymentForm);
也可以由前端获取表单,将其写入页面后提交。采用这种方式时,必须确保返回内容只来自可信的支付服务端:
<div id="payment-container"></div>
<script>
async function startPayment() {
const response = await fetch("/api/payment/alipay", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
orderId: "ORDER_202609090001"
})
});
const formHtml = await response.text();
const container = document.getElementById("payment-container");
container.innerHTML = formHtml;
const form = container.querySelector("form");
if (form) {
form.submit();
}
}
</script>
<button type="button" onclick="startPayment()">支付宝支付</button>
更稳妥的方式是让用户点击支付按钮后直接进入后端支付地址,再由后端输出支付表单:
<button type="button" onclick="location.href='/pay/alipay?orderId=ORDER_202609090001'">
支付宝支付
</button>
这样能减少异步请求、弹窗策略和 WebView 安全限制对应用唤起流程的影响。
3. 保留中间页和网页支付作为降级方案
如果支付宝返回的支付流程包含中间页,通常不应通过 DOM 操作、自动点击或脚本跳转将其绕过。页面应允许用户选择:
- 打开支付宝
- 下载或安装支付宝
- 返回商户页面
- 在支持的情况下继续网页支付
如果业务要求完全不显示中间页,应先咨询支付宝技术支持,确认当前接入的支付产品是否提供适用于鸿蒙环境的官方 App Link、SDK 或其他唤起方案。只修改 H5 JavaScript 通常无法满足这一要求。
不建议直接拼接支付宝 Scheme
下面这种写法可能在部分设备上有效,但无法保证兼容性和稳定性:
location.href = "alipays://platformapi/startapp?...";
这种方式可能遇到以下问题:
- 浏览器阻止并非由用户操作触发的外部应用跳转
- 参数格式或入口发生变化
- 未安装支付宝时无法正确降级
- 内置 WebView 禁止打开自定义 Scheme
- 鸿蒙系统无法找到对应的应用处理程序
- 自行拼接支付参数可能带来签名、订单篡改或重复支付风险
除非支付宝对应产品的官方接入文档明确要求,否则不要将自定义 Scheme 用作正式支付入口。
排查步骤
可以按以下顺序排查:
- 确认已经接入支付宝正式提供的手机网站支付接口,没有自行拼接 Scheme。
- 确认支付请求由用户点击按钮触发,避免在页面加载后自动唤起支付宝。
- 分别使用系统浏览器、第三方浏览器和应用内 WebView 测试。
- 确认设备已经安装可以正常使用的支付宝客户端。
- 记录鸿蒙系统类型、系统版本、浏览器名称、支付宝版本和完整的跳转链路。
- 检查中间页域名是否为支付宝官方域名,排除业务代理页或第三方跳转服务。
- 使用支付宝开放平台的沙箱或测试订单复现问题,但不要只根据沙箱结果判断正式环境的表现。
- 如果中间页来自支付宝官方收银台,将复现信息及订单请求参数中的非敏感内容提交给支付宝技术支持确认。
注意事项
判断支付成功时,不能只依赖 return_url 页面或前端跳转结果。用户可能关闭页面,浏览器也可能丢失回跳。订单的最终状态应以支付宝异步通知为准,服务端需要完成验签、金额核对、商户身份核对和幂等处理。
不要仅根据 User-Agent 判断“鸿蒙系统就改用 Scheme”。浏览器兼容模式可能会修改 User-Agent,而且 User-Agent 无法准确反映支付宝是否已经安装。环境识别可以用于优化界面提示,但不能作为支付安全判断的依据。
业务侧可以优化支付入口和接入方式,但无法保证鸿蒙系统始终不显示中间页。可靠的做法是遵循支付宝官方 H5 支付流程,并允许浏览器或收银台在无法安全直达客户端时采用降级方案。
备注:内容仅供参考。
