iOS WKWebView 嵌入网页后无法唤起支付宝支付
结论
WKWebView 不会自动处理支付宝客户端使用的自定义 URL Scheme。遇到 target="_blank"、window.open() 或跨应用跳转时,也可能不会继续导航。因此,网页跳转到 alipays:// 等地址时,看起来会毫无反应。
排查时,先确认支付宝返回的支付链接有效。然后在 WKNavigationDelegate 中识别外部支付 URL,并交给 UIApplication 打开。新窗口请求、回跳地址和失败回调也需要一并处理。
如果使用支付宝移动支付 SDK,应直接调用 SDK,不要把 SDK 支付流程当成普通网页加载。选择 H5 支付还是 App 支付,要看商户签约的产品以及服务端创建订单所用的接口。
先确认问题发生在哪一层
可以使用 Safari Web Inspector,或在导航代理中打印 URL:
print("WKWebView navigation URL:", navigationAction.request.url?.absoluteString ?? "nil")
主要检查以下情况:
- 点击支付按钮后完全没有导航请求,通常是 JavaScript 报错、新窗口没有处理,或点击事件被页面逻辑阻止。
- 已进入支付收银台,但停在某个中间页面,需要检查重定向、Cookie、User-Agent 和网络请求。
- 出现
alipay://或alipays://URL,但客户端没有打开,需要让原生代码接管。 UIApplication.open返回失败,可能是支付宝未安装,或 URL 不符合预期。- 支付宝已经打开,但支付后无法返回 App,需要配置并处理回跳 Scheme 或 Universal Link。
- 服务端下单已经失败,这与
WKWebView无关,应先检查支付接口响应、签名和订单参数。
不要只查看最初加载的支付页面地址。跳转到支付宝客户端的 URL 往往要经过多次 HTTP 重定向才会出现。
在 WKWebView 中接管支付宝跳转
下面的 Swift 示例同时处理普通导航和 target="_blank"。Scheme 白名单应以实际支付流程中捕获的地址为准,不要放行任意非 HTTP URL。
import UIKit
import WebKit
final class PayViewController: UIViewController,
WKNavigationDelegate,
WKUIDelegate {
private lazy var webView: WKWebView = {
let configuration = WKWebViewConfiguration()
configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
let webView = WKWebView(
frame: .zero,
configuration: configuration
)
webView.navigationDelegate = self
webView.uiDelegate = self
return webView
}()
override func viewDidLoad() {
super.viewDidLoad()
webView.frame = view.bounds
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(webView)
guard let url = URL(string: "https://example.com/pay") else {
return
}
webView.load(URLRequest(url: url))
}
func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
guard let url = navigationAction.request.url else {
decisionHandler(.cancel)
return
}
let scheme = url.scheme?.lowercased() ?? ""
let allowedPaymentSchemes: Set<String> = [
"alipay",
"alipays"
]
if allowedPaymentSchemes.contains(scheme) {
decisionHandler(.cancel)
UIApplication.shared.open(
url,
options: [:]
) { success in
if !success {
// 在这里提示用户安装支付宝,或进入支付失败处理流程。
print("Unable to open Alipay URL")
}
}
return
}
decisionHandler(.allow)
}
func webView(
_ webView: WKWebView,
createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures
) -> WKWebView? {
// target="_blank" 或 window.open() 通常没有目标 frame。
if navigationAction.targetFrame == nil,
let url = navigationAction.request.url {
webView.load(URLRequest(url: url))
}
return nil
}
}
每条代码路径都必须调用一次 decisionHandler,而且只能调用一次。否则,页面可能卡住或出现运行时警告。
配置支付宝安装检测
如果通过 canOpenURL(_:) 检测支付宝是否安装,需要在 Info.plist 中声明要查询的 Scheme:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>alipay</string>
<string>alipays</string>
</array>
例如:
if let url = URL(string: "alipays://"),
UIApplication.shared.canOpenURL(url) {
print("Alipay may be available")
}
LSApplicationQueriesSchemes 主要影响 canOpenURL(_:) 的查询结果。完成这项配置后,WKWebView 仍不会自动唤起支付宝,实际跳转还要通过 UIApplication.open 处理。
Scheme 的取值应根据支付流程中实际捕获的 URL 和当前支付宝接入要求确定。不要为了所谓的兼容性加入大量无关 Scheme。
处理支付完成后的 App 回跳
如果支付完成后需要返回 App,应在支付请求中配置服务端允许的回跳地址,并在应用中注册相应的 URL Scheme 或 Associated Domains。不要将未经编码的回跳 URL 直接拼接到支付链接中。
使用自定义 Scheme 时,可以在应用生命周期回调中接收返回地址:
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
print("Payment callback URL:", url.absoluteString)
return true
}
采用 Scene 生命周期的应用还要处理:
func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
) {
guard let url = URLContexts.first?.url else {
return
}
print("Payment callback URL:", url.absoluteString)
}
客户端回跳只能用于恢复界面或触发订单查询,不能作为支付成功的最终依据。支付结果应以服务端异步通知或服务端主动查询的结果为准。
如果接入的是支付宝移动支付 SDK
App 支付与 H5 支付的调用方式不同:
- H5 支付会加载服务端生成的网页支付地址,并处理网页跳转到支付宝客户端的过程。
- App 支付由服务端生成并签名订单信息,再由 iOS 端调用支付宝 SDK。
- 两者的签约产品、下单接口、请求参数和回调方式可能不同。
H5 跳转失败后,不能直接截取某段 URL,再将其作为 payOrder 参数调用 SDK。如果准备改用移动支付 SDK,需要同步调整服务端的订单创建和签名流程,并按照当前 SDK 接入文档配置回跳。
仍然无法唤起时的检查清单
- 使用真机测试。模拟器不能反映支付宝客户端的实际跳转行为。
- 确认设备已经安装支付宝,并且可以正常打开。
- 记录每一次
WKNavigationAction,检查是否出现支付宝 Scheme。 - 检查页面是否通过
window.open()或target="_blank"打开收银台。 - 检查
UIApplication.open的完成回调是否为false。 - 确认支付地址来自可信服务端,并检查订单创建、签名和商户产品权限是否正常。
- 不要把所有非
http、https地址都交给系统打开。应使用明确的白名单并校验来源。 - 不要在客户端生成或保存商户私钥。
- 如果流程涉及特定 HTTPS 域名跳转到外部应用,只处理支付宝支付流程中的已知域名,避免将普通网页链接全部交给 Safari 打开。
- 如果同一地址在 Safari 中可以唤起支付宝,但在
WKWebView中不行,应优先检查导航代理、新窗口处理和外部 Scheme 接管逻辑。
备注:内容仅供参考。
