PAYATHON 2026

未安装支付宝客户端时无法唤起 H5 支付

支付小周

结论

processOrderWithPaymentResult:standbyCallback: 不是发起支付的接口,只用于处理支付宝返回 App 的 URL。如果用它来“开始支付”,SDK 不会主动打开 H5 收银台。未安装支付宝客户端时,页面也就不会被唤起。

发起支付应调用:

[[AlipaySDK defaultService] payOrder:orderString
                         fromScheme:appScheme
                            callback:^(NSDictionary *resultDic) {
    // 处理同步返回结果
}];

此外,还要正确配置 URL Scheme,并在 AppDelegate 或 SceneDelegate 中调用 processOrderWithPaymentResult:standbyCallback: 处理支付回跳。设置 UIApplication.windows[0] 无法代替这些步骤,也不建议通过窗口数组下标解决这个问题。

原因说明

这两个 API 的用途不同:

  • payOrder:fromScheme:callback::用于发起支付。已安装支付宝客户端时,SDK 会优先拉起客户端;未安装时,SDK 才可能按照其支持的流程进入 H5 收银台。
  • processOrderWithPaymentResult:standbyCallback::用于解析支付宝通过 URL Scheme 返回 App 的支付结果,不会创建订单,也不会打开支付页面。

如果安装和未安装支付宝客户端时的表现不同,应优先检查:

  1. 是否误把结果处理 API 当成了支付 API。
  2. orderString 是否为服务端生成的完整 App 支付订单串。
  3. fromScheme: 传入的 Scheme 是否与工程配置完全一致。
  4. URL 回调是否同时适配了 AppDelegate 和 iOS 13 之后的 SceneDelegate。
  5. 支付调用是否在主线程执行。
  6. SDK 版本是否过旧,是否与目标 iOS 版本或多场景生命周期存在兼容问题。

正确的支付调用方式

订单信息应由服务端调用支付宝接口并完成签名,客户端只接收最终的 orderString。不要在 App 内保存支付宝应用私钥,也不要在客户端使用私钥签名。

#import <AlipaySDK/AlipaySDK.h>

- (void)startAlipayWithOrderString:(NSString *)orderString {
    if (orderString.length == 0) {
        return;
    }

    NSString *appScheme = @"your_alipay_app_scheme";

    dispatch_async(dispatch_get_main_queue(), ^{
        [[AlipaySDK defaultService] payOrder:orderString
                                 fromScheme:appScheme
                                    callback:^(NSDictionary *resultDic) {
            NSLog(@"Alipay result: %@", resultDic);

            NSString *resultStatus = [NSString stringWithFormat:@"%@", resultDic[@"resultStatus"]];
            if ([resultStatus isEqualToString:@"9000"]) {
                // 客户端结果仅用于界面提示
                // 最终支付状态仍应向业务服务端查询
            } else {
                // 根据 resultStatus 展示取消、失败或处理中状态
            }
        }];
    });
}

请将 your_alipay_app_scheme 替换为当前 App 使用的唯一 Scheme。

配置 URL Scheme

在 Xcode 的 Target 配置中打开:

Info
→ URL Types
→ URL Schemes

添加与 fromScheme: 参数完全一致的值,例如:

your_alipay_app_scheme

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

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

不要使用容易与其他 App 冲突的通用名称作为 Scheme。

处理支付宝回跳

使用 AppDelegate

#import <AlipaySDK/AlipaySDK.h>

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {

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

    return YES;
}

如果项目还需要兼容旧版生命周期,可以保留相应的旧式 openURL 回调,但不要在多个入口重复处理同一个 URL。

使用 SceneDelegate

启用 Scene 生命周期后,应在 SceneDelegate 中处理回跳:

#import <AlipaySDK/AlipaySDK.h>

- (void)scene:(UIScene *)scene
    openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
    API_AVAILABLE(ios(13.0)) {

    NSURL *url = URLContexts.allObjects.firstObject.URL;
    if (url == nil) {
        return;
    }

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

如果同一个工程可能同时经过 AppDelegate 和 SceneDelegate,可以将 URL 处理逻辑封装为统一方法,以免遗漏或重复回调。

不要依赖 windows[0]

下面的处理方式并不可靠:

[[[UIApplication sharedApplication] windows] objectAtIndex:0]

在多窗口和 UIScene 环境中,数组里的第一个窗口不一定是当前活动窗口,数组也不一定非空。更重要的是,手动显示窗口不会让 processOrderWithPaymentResult:standbyCallback: 获得发起支付的能力。

如果业务确实需要获取当前窗口,应从活动的 UIWindowScene 中查找,例如:

- (UIWindow *)activeWindow {
    for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
        if (scene.activationState != UISceneActivationStateForegroundActive) {
            continue;
        }

        if (![scene isKindOfClass:UIWindowScene.class]) {
            continue;
        }

        UIWindowScene *windowScene = (UIWindowScene *)scene;
        for (UIWindow *window in windowScene.windows) {
            if (window.isKeyWindow) {
                return window;
            }
        }
    }

    return nil;
}

正常接入支付宝 SDK 时,不应通过修改窗口数组来强行显示 H5 页面,而应调用 SDK 提供的公开支付接口。

检查订单串

客户端必须传入由服务端生成并签名的完整 App 支付订单串。如果服务端生成的是手机网站支付订单、网页跳转 URL,或者配置了错误的订单产品类型,就不能直接将其作为 payOrder: 的 orderString 使用。

需要确认:

  • 服务端使用的是支付宝 App 支付对应的接口和产品配置。
  • 签名、字符编码和参数拼接是否完整。
  • orderString 是否被重复进行 URL 编码。
  • 客户端是否删除、拆分或重新排列了订单参数。
  • 商户应用是否已开通对应的支付能力。

即使 H5 页面能够打开,也不能仅凭客户端回调判断支付成功。最终支付结果应由业务服务端通过支付宝异步通知或主动查询来确认。

SDK 版本处理

AlipaySDK-iOS 15.8.16 已经不是较新的集成版本。仅凭当前现象,无法断定这个版本一定存在 H5 唤起缺陷。不过,在确认调用方式、订单串和 URL Scheme 都正确后,建议升级到支付宝官方当前提供的稳定版本,并根据对应版本的接入文档重新检查生命周期适配。

如果通过 CocoaPods 管理依赖,可以先确认项目实际解析到的版本,再按团队的依赖管理策略更新。不要在没有完成回归测试时直接放宽所有依赖的版本限制。

升级后至少应验证这些场景:

  • 已安装支付宝客户端。
  • 未安装支付宝客户端。
  • 支付成功、取消和失败。
  • H5 收银台返回 App。
  • 冷启动和后台唤醒。
  • AppDelegate 与 SceneDelegate 生命周期。
  • 真机上的不同 iOS 版本。

如果改用 payOrder:fromScheme:callback: 后仍然完全无法打开 H5 页面,应记录 resultDic、订单生成方式、真机 iOS 版本和 SDK 日志,再联系支付宝技术支持,确认该 SDK 版本、商户产品配置以及当前订单是否支持无客户端支付。

备注:内容仅供参考。