手机网站和app的webview如何通过alipays唤起支付宝APP
结论
在普通手机浏览器中,可以尝试通过 alipays:// 协议唤起支付宝。如果页面运行在 App 的 WebView 内,通常还需要宿主 App 拦截 alipays:// 导航,并调用 Android 或 iOS 的系统 API 打开支付宝,因为 WebView 一般不会自动处理自定义 Scheme。
处理流程如下:
- H5 页面触发
alipays://链接。 - WebView 拦截链接。
- 宿主 App 判断支付宝是否已安装。
- 宿主 App 调用系统能力打开支付宝。
- 打开失败时提示用户,或跳转到备用页面。
如果用于正式支付,不建议自行拼接支付参数。支付链接、订单信息和签名应由服务端按照支付宝当前的官方接入方案生成。
H5 页面触发支付宝
H5 页面可以在用户点击按钮后,跳转到后端返回的支付宝链接:
<button id="open-alipay">打开支付宝</button>
<script>
document.getElementById('open-alipay').addEventListener('click', function () {
const alipayUrl =
'alipays://platformapi/startapp?appId=YOUR_APP_ID';
window.location.href = alipayUrl;
});
</script>
这里的地址只用于演示 Scheme 的调用方式。appId、业务参数和完整链接都必须替换为实际接入方案提供的内容。
建议由用户主动点击按钮后再发起跳转。部分系统或 WebView 会限制页面加载完成后自动唤起外部 App。
Android WebView 处理方式
Android 需要在 WebViewClient 中拦截非 HTTP/HTTPS 地址。检测到 alipays Scheme 后,通过 Intent 交给系统处理:
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(
WebView view,
WebResourceRequest request
) {
return handleUrl(request.getUrl().toString());
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return handleUrl(url);
}
private boolean handleUrl(String url) {
Uri uri = Uri.parse(url);
String scheme = uri.getScheme();
if ("alipays".equalsIgnoreCase(scheme)) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setPackage("com.eg.android.AlipayGphone");
webView.getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(
webView.getContext(),
"未检测到支付宝,请先安装支付宝",
Toast.LENGTH_SHORT
).show();
}
return true;
}
return false;
}
});
调用 setPackage() 后,Intent 只会交给支付宝处理,可以降低链接被其他应用接管的风险。如果业务需要兼容不同地区或不同版本的支付宝客户端,应以相应产品的官方接入文档为准,不能只根据包名判断。
Kotlin 写法如下:
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView,
request: WebResourceRequest
): Boolean {
return handleUrl(view, request.url.toString())
}
@Suppress("DEPRECATION")
override fun shouldOverrideUrlLoading(
view: WebView,
url: String
): Boolean {
return handleUrl(view, url)
}
private fun handleUrl(view: WebView, url: String): Boolean {
val uri = Uri.parse(url)
if (uri.scheme.equals("alipays", ignoreCase = true)) {
val intent = Intent(Intent.ACTION_VIEW, uri).apply {
addCategory(Intent.CATEGORY_BROWSABLE)
setPackage("com.eg.android.AlipayGphone")
}
try {
view.context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(
view.context,
"未检测到支付宝,请先安装支付宝",
Toast.LENGTH_SHORT
).show()
}
return true
}
return false
}
}
shouldOverrideUrlLoading() 返回 true,表示本次导航已经由 App 接管。返回 false 时,WebView 会继续尝试加载该地址,通常会出现“无法打开网页”或“不支持的协议”等错误。
iOS WKWebView 处理方式
iOS 可以使用 WKNavigationDelegate 拦截导航,再通过 UIApplication.open 打开支付宝:
import UIKit
import WebKit
final class PaymentViewController: UIViewController, WKNavigationDelegate {
private var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView(frame: view.bounds)
webView.navigationDelegate = self
view.addSubview(webView)
}
func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
guard let url = navigationAction.request.url else {
decisionHandler(.cancel)
return
}
if url.scheme?.lowercased() == "alipays" {
decisionHandler(.cancel)
UIApplication.shared.open(
url,
options: [:]
) { success in
if !success {
// 在这里提示用户安装支付宝,或者展示备用支付入口。
}
}
return
}
decisionHandler(.allow)
}
}
如果还要使用 canOpenURL() 提前判断能否打开,通常需要在 Info.plist 中配置查询白名单:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>alipays</string>
</array>
判断代码如下:
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:])
} else {
// 未安装支付宝或系统不允许打开
}
无论进入哪个分支,decisionHandler 都只能调用一次,否则可能引发异常。
增加来源校验
不要把所有非 HTTP Scheme 都交给系统处理。至少应限制允许打开的协议,并按需检查来源域名:
private boolean isTrustedAlipayUrl(String url) {
Uri uri = Uri.parse(url);
return "alipays".equalsIgnoreCase(uri.getScheme());
}
还可以进一步校验:
- 当前 WebView 页面是否来自自己的 HTTPS 域名。
- 跳转是否由用户点击触发。
- Scheme 中的目标路径和参数是否符合预期。
- 支付订单是否由可信服务端生成。
- 是否存在重复点击或重复拉起。
缺少这些限制时,WebView 中加载的第三方页面也可能借助宿主 App 打开外部应用。
支付场景的处理方式
如果目标是完成订单支付,而不只是打开支付宝,需要先确定使用哪种官方产品,例如 H5 支付或支付宝客户端 SDK。两者的处理方式不同:
- H5 支付通常由服务端创建订单,并返回支付页面或跳转信息。
- App 支付通常由原生客户端接入支付宝 SDK。
- 在 WebView 中嵌入 H5 支付时,还要处理支付宝返回地址、支付结果查询和 App 回跳。
- 前端跳转成功只表示支付宝已经打开,不表示支付成功。
不能只根据前端回调或页面跳转判断支付结果。最终订单状态应由服务端结合支付宝异步通知和主动查询进行确认。
常见问题
浏览器里能唤起,WebView 里没有反应
浏览器通常已经实现了外部协议处理,而 WebView 默认只负责网页导航。因此,需要通过 Android 的 shouldOverrideUrlLoading() 或 iOS 的 WKNavigationDelegate 主动接管链接。
Android 提示找不到可处理的应用
支付宝可能没有安装,也可能是 Scheme 不正确,或者目标客户端与指定包名不匹配。应捕获 ActivityNotFoundException,并为用户提供备用入口。
iOS 的 canOpenURL() 一直返回 false
检查 LSApplicationQueriesSchemes 中是否包含 alipays,同时确认测试设备已经安装支付宝。模拟器通常不能代表真实设备上的唤起结果。
已经打开支付宝,但订单状态没有更新
打开外部 App 和支付成功是两件事。用户可能取消支付、切换应用,也可能支付失败。App 恢复到前台后,应重新向业务服务端查询订单状态,不能直接将订单标记为已支付。
是否可以使用定时器判断支付宝有没有打开
H5 可以借助 setTimeout、visibilitychange 或页面失焦进行辅助判断,但这些方式无法可靠证明支付宝已经安装或支付成功。不同系统、WebView 和浏览器的行为并不一致,业务状态仍应以服务端返回的结果为准。
备注:内容仅供参考。
