PAYATHON 2026

iOS WKWebView 调用 AlipaySDK 无法拉起支付宝客户端

支付阿杰

结论

这段 WKNavigationDelegate 拦截代码的整体逻辑没有明显问题。payInterceptorWithUrl:fromScheme:callback: 返回 YES 时取消当前导航,否则继续加载,这种处理是正确的。

支付宝客户端无法拉起,通常不是 decisionHandler 或回调代码本身导致的。建议重点检查以下几项:

  1. fromScheme 与 App 实际注册的 URL Scheme 不一致;
  2. H5 跳转 URL 不是 AlipaySDK 能识别的支付宝收银台 URL;
  3. App 没有配置支付宝回跳所需的 URL Scheme;
  4. AppDelegate 或 SceneDelegate 没有将回跳 URL 交给 AlipaySDK;
  5. 支付页面通过 target="_blank"、window.open() 或新窗口发起跳转,没有进入当前导航代理;
  6. AlipaySDK 版本过旧,与当前 iOS 或 WKWebView 的行为不兼容。

Android 能正常拉起,只能说明 H5 支付链路基本可用,不能证明 iOS 端的 Scheme、SDK 接入和回跳配置没有问题。

首先检查 fromScheme

示例中的:

fromScheme:@"alisdkdemo"

是支付宝示例工程使用的 Scheme。业务 App 不能直接照搬,必须替换为自己在 Xcode 中注册的 URL Scheme,例如:

fromScheme:@"myappalipay"

Target 中也要配置相同的值:

Target
→ Info
→ URL Types
→ URL Schemes

还可以检查 Info.plist 中是否有对应配置:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myappalipay</string>
        </array>
    </dict>
</array>

代码、Info.plist 和支付请求使用的 Scheme 必须完全一致,大小写也要相同。建议使用与 Bundle ID 相关的唯一值,避免和其他 App 冲突。

推荐的 WKWebView 拦截写法

调用 SDK 前,可以输出实际导航 URL 和拦截结果,确认支付 URL 是否确实进入代理:

- (void)webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSURL *URL = navigationAction.request.URL;
    NSString *urlString = URL.absoluteString;

    NSLog(@"WKWebView navigation URL: %@", urlString);

    if (urlString.length == 0) {
        decisionHandler(WKNavigationActionPolicyAllow);
        return;
    }

    __weak typeof(self) weakSelf = self;

    BOOL intercepted = [[AlipaySDK defaultService]
        payInterceptorWithUrl:urlString
        fromScheme:@"myappalipay"
        callback:^(NSDictionary *result) {
            NSLog(@"Alipay interceptor result: %@", result);

            BOOL processed = [result[@"isProcessUrlPay"] boolValue];
            NSString *returnURL = result[@"returnUrl"];

            if (processed && returnURL.length > 0) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [weakSelf loadWithUrlStr:returnURL];
                });
            }
        }];

    NSLog(@"Alipay intercepted: %@", intercepted ? @"YES" : @"NO");

    if (intercepted) {
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }

    decisionHandler(WKNavigationActionPolicyAllow);
}

需要注意:

  • 每条执行路径只能调用一次 decisionHandler;
  • SDK 返回 YES 后,必须取消 WebView 当前导航;
  • 不要自行根据 URL 中是否包含 alipay 来决定是否拦截,应以 SDK 返回值为准;
  • returnUrl 可能为空,使用前要先判断;
  • 更新 UI 或重新加载页面时,应切换到主线程。

配置支付宝回跳处理

App 从支付宝客户端返回时,需要把 URL 交给 AlipaySDK。使用 AppDelegate 生命周期的项目可以这样配置:

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
    if ([url.host isEqualToString:@"safepay"]) {
        [[AlipaySDK defaultService]
            processOrderWithPaymentResult:url
            standbyCallback:^(NSDictionary *resultDic) {
                NSLog(@"Alipay result: %@", resultDic);
            }];
    }

    return YES;
}

如果项目还需要兼容旧版 AppDelegate 回调,也要确保对应入口能正常转发:

- (BOOL)application:(UIApplication *)application
             openURL:(NSURL *)url
   sourceApplication:(NSString *)sourceApplication
          annotation:(id)annotation
{
    if ([url.host isEqualToString:@"safepay"]) {
        [[AlipaySDK defaultService]
            processOrderWithPaymentResult:url
            standbyCallback:^(NSDictionary *resultDic) {
                NSLog(@"Alipay result: %@", resultDic);
            }];
    }

    return YES;
}

采用 Scene 生命周期的项目,还要检查 SceneDelegate:

- (void)scene:(UIScene *)scene
openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
{
    NSURL *url = URLContexts.allObjects.firstObject.URL;
    if (!url) {
        return;
    }

    if ([url.host isEqualToString:@"safepay"]) {
        [[AlipaySDK defaultService]
            processOrderWithPaymentResult:url
            standbyCallback:^(NSDictionary *resultDic) {
                NSLog(@"Alipay result: %@", resultDic);
            }];
    }
}

具体使用哪个 AlipaySDK 结果处理 API,要以项目当前集成版本的 SDK 文档和头文件为准。不同版本可能还提供授权结果、通用链接等入口,不能将其他业务的回调方法用于处理支付结果。

检查 H5 支付 URL 是否被 SDK 识别

payInterceptorWithUrl:fromScheme:callback: 不是用于唤起任意 URL 的通用接口。它只能处理符合支付宝 H5 支付流程,并且能被当前 SDK 识别的 URL。

建议记录这些信息:

NSLog(@"request URL: %@", navigationAction.request.URL.absoluteString);
NSLog(@"navigationType: %ld", (long)navigationAction.navigationType);
NSLog(@"targetFrame: %@", navigationAction.targetFrame);
NSLog(@"intercepted: %@", intercepted ? @"YES" : @"NO");

可以根据日志判断问题所在:

  • 如果代理方法没有执行,问题出在 WKWebView 导航链路;
  • 如果代理执行了,但 intercepted == NO,说明 SDK 没有识别该 URL,需要检查服务端返回的支付链接、产品类型和签约能力;
  • 如果 intercepted == YES,但客户端没有拉起,应重点检查 Scheme、SDK 版本、设备环境和系统日志;
  • 如果能拉起支付宝,但无法返回 App,需要检查 URL Types 和回跳处理。

不要在客户端拼接、修改支付 URL,也不要解码后再重新组装。支付宝返回的跳转地址通常带有签名和编码参数,任何改动都可能导致 SDK 无法识别。

处理 target="_blank" 和 window.open()

部分 H5 收银台会通过新窗口打开支付链接。此时 navigationAction.targetFrame 可能为 nil。如果没有实现 WKUIDelegate,这次跳转可能直接丢失。

先设置代理:

webView.UIDelegate = self;

再处理新窗口请求:

- (WKWebView *)webView:(WKWebView *)webView
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
   forNavigationAction:(WKNavigationAction *)navigationAction
        windowFeatures:(WKWindowFeatures *)windowFeatures
{
    if (navigationAction.targetFrame == nil) {
        NSURLRequest *request = navigationAction.request;
        if (request.URL) {
            [webView loadRequest:request];
        }
    }

    return nil;
}

这样,新窗口请求会重新交给当前 WKWebView,随后进入 decidePolicyForNavigationAction:,支付宝 SDK 才能进行拦截。

如果 H5 使用 JavaScript 生成中间页,还要确认 JavaScript 没有被禁用:

WKWebViewConfiguration *configuration =
    [[WKWebViewConfiguration alloc] init];

configuration.defaultWebpagePreferences.allowsContentJavaScript = YES;

检查 URL 查询白名单

如果业务代码或某个 SDK 版本会通过 canOpenURL: 检查支付宝客户端,需要在 Info.plist 中配置被查询的 Scheme。具体 Scheme 列表应以当前 AlipaySDK 接入文档为准,不建议凭经验随意添加。

常见配置如下:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>alipay</string>
    <string>alipays</string>
</array>

这项配置主要影响 canOpenURL: 查询。缺少白名单不一定会阻止所有 openURL: 调用,因此它不能代替 URL Types、回跳处理和正确的支付 URL。

还需要排除的环境问题

建议使用真机测试,并逐项确认:

  • 真机已经安装可以正常使用的支付宝客户端;
  • App 使用当前官方渠道提供的 AlipaySDK;
  • SDK 依赖的系统库、-ObjC 等工程配置符合对应版本的接入说明;
  • 没有其他 WKWebView 代理或中间层提前调用 decisionHandler;
  • 支付 URL 没有被交给错误的 WebView 实例;
  • fromScheme 不是示例值,并且与 App 的实际配置一致;
  • 服务端创建的订单适用于当前 H5 支付场景,而不是只适用于 Android 或其他产品的调用方式。

建议的排查顺序

按照以下顺序检查,通常可以较快找到问题:

  1. 打印每一次 navigationAction.request.URL;
  2. 确认支付 URL 是否进入 decidePolicyForNavigationAction:;
  3. 记录 payInterceptorWithUrl: 的布尔返回值;
  4. 将 alisdkdemo 替换为业务 App 的真实 URL Scheme;
  5. 核对 Xcode 中的 URL Types 配置;
  6. 补充 AppDelegate 或 SceneDelegate 的支付宝回跳处理;
  7. 处理 target="_blank" 和 window.open();
  8. 使用当前 AlipaySDK 版本在真机上重新测试;
  9. 如果 SDK 始终返回 NO,将脱敏后的原始支付 URL、SDK 版本、iOS 版本和完整控制台日志提交给支付宝技术支持,由其核对支付产品和链接格式。

仅凭现有代码,还不能断定问题出在支付宝客户端或 SDK 内部。排查时最需要确认的是:支付 URL 是否进入代理,SDK 返回 YES 还是 NO,以及 fromScheme 是否与 App 注册的 URL Scheme 完全一致。

备注:内容仅供参考。