APP内嵌Webview打开H5支付页时Android无法唤起支付宝
结论
Android 的 WebView 通常不会自动处理支付宝的 alipays://、intent:// 等跳转协议,而系统浏览器和部分 iOS WebView 可能会处理。支付页尝试唤起支付宝时,如果 APP 没有在 WebViewClient 中拦截这些链接并交给系统处理,唤起就会失败,页面随后会降级为“复制口令支付”。
这类问题通常与支付宝账号或支付订单无关,原因在于 Android WebView 处理外部应用协议的方式不同。
产生差异的原因
支付宝 H5 支付页唤起客户端时,可能使用以下链接:
alipays://platformapi/startapp?...
或者:
intent://platformapi/startapp?...#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end
普通的 https:// 链接可以继续在 WebView 中加载,自定义协议则需要交给 Android 系统的 Intent 处理。WebView 默认不会替宿主 APP 执行这一步。
iOS 使用另一套 URL Scheme 或 Universal Link 调度机制,实际表现还会受 WebView 类型、支付宝版本和支付产品配置影响。iOS 能唤起支付宝,并不意味着 Android 也会自动唤起。
“复制口令”一般是支付页发现无法直接打开支付宝客户端后启用的兼容方案。
Android 处理方式
可以在 WebViewClient 中拦截非 HTTP 链接,并通过系统 Intent 处理支付宝协议。下面是一个 Kotlin 示例:
private const val ALIPAY_PACKAGE = "com.eg.android.AlipayGphone"
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView,
request: WebResourceRequest
): Boolean {
return handleUrl(view.context, request.url.toString())
}
@Suppress("DEPRECATION")
override fun shouldOverrideUrlLoading(
view: WebView,
url: String
): Boolean {
return handleUrl(view.context, url)
}
}
private fun handleUrl(context: Context, url: String): Boolean {
return when {
url.startsWith("http://") || url.startsWith("https://") -> {
false
}
url.startsWith("alipays://") || url.startsWith("alipay://") -> {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setPackage(ALIPAY_PACKAGE)
}
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
// 未安装支付宝,保留支付页自身的口令或下载引导
}
true
}
url.startsWith("intent://") -> {
try {
val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setPackage(ALIPAY_PACKAGE)
}
context.startActivity(intent)
} catch (e: Exception) {
// intent 格式无效、未安装支付宝或系统无法处理
}
true
}
else -> {
// 不要直接打开任意自定义协议
false
}
}
}
同时需要确认 WebView 的基础能力已经启用:
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
是否必须启用 JavaScript 和 DOM Storage,取决于具体的支付页面,不过多数 H5 收银台会依赖这些能力。
Android 11 及以上的应用可见性
如果业务代码需要通过 PackageManager 检查支付宝是否已安装,Android 11 及以上还要在 AndroidManifest.xml 中声明查询范围:
<manifest>
<queries>
<package android:name="com.eg.android.AlipayGphone" />
</queries>
<application>
<!-- ... -->
</application>
</manifest>
这项配置主要影响“是否安装”的查询结果。添加配置后,WebView 仍不会自动唤起支付宝,APP 依然需要拦截协议并调用 startActivity()。
建议的排查步骤
- 在
shouldOverrideUrlLoading()中记录支付过程产生的 URL,确认其中是否有alipays://或intent://。 - 如果完全没有出现唤起协议,检查支付接口返回内容、支付产品类型、User-Agent 和支付宝商户侧配置。
- 如果出现了协议却没有跳转,检查回调是否返回
true,以及代码是否实际调用了startActivity()。 - 捕获
ActivityNotFoundException,确认设备是否安装支付宝,以及支付宝是否被系统停用。 - 分别使用系统浏览器和 APP WebView 测试。同一订单能在浏览器中唤起支付宝,却无法在 WebView 中唤起,通常说明问题出在宿主 APP 的协议处理上。
- 如果是原生 APP 支付场景,优先评估支付宝 APP 支付 SDK,不要只为复用网页流程而强行使用 H5 支付。
注意事项
不要把所有非 http、https 链接直接传给 Intent。支付页面属于外部内容,如果不加限制地打开任意 Scheme,可能带来安全风险。应限制允许使用的协议和目标包名,并校验 intent:// 中的组件、包名和回退地址。
也不建议伪造浏览器 User-Agent,强制支付页使用某种唤起流程。这样做可能绕过页面的环境判断,却无法解决 WebView 不处理自定义协议的问题,还可能引发回跳、风控或兼容性异常。
如果接入的是支付宝特定 H5 支付产品,还要遵守该产品当前的接入条件。部分能力可能对 WebView、域名、回跳地址或客户端环境另有要求,修改 Android 代码不能替代正确的商户侧配置。
备注:内容仅供参考。
