PAYATHON 2026

iOS 集成支付 SDK 时 payOrder 无法唤起支付如何排查?

支付小周

结论

从现有现象看,Universal Link 和 H5 支付链路基本可用,问题更可能出在已安装支付宝时的客户端支付链路。常见原因包括:

  • payOrder 收到的订单字符串不合法、为空或经过二次编码;
  • fromScheme 与商户 App 注册的 URL Scheme 不一致;
  • 支付宝回跳事件没有正确传给 SDK;
  • payOrder 的调用线程、SDK 集成方式或客户端环境存在异常;
  • 服务端签名、应用身份或订单参数校验失败。

能够打开支付宝 App,只能说明应用跳转成功,不能证明支付宝客户端已经接受订单。排查时应先检查 payOrder 入参、回跳配置和 SDK 返回结果。

1. 检查 payOrder 的订单字符串

传给 payOrder 的参数应是服务端生成并签名的完整订单字符串。客户端不要重新拼接、排序、签名或执行 URL Encode。

调用前可以记录以下信息:

NSString *orderString = response.orderString;

NSLog(@"orderString is nil: %@", orderString == nil ? @"YES" : @"NO");
NSLog(@"orderString length: %lu", (unsigned long)orderString.length);
NSLog(@"orderString: %@", orderString);

需要检查:

  • 是否为 nil 或空字符串;
  • 是否带有换行或首尾空格;
  • 是否经过 JSON 转义;
  • %、&、=、+、引号等字符是否被重复编码;
  • 服务端返回的是完整订单字符串,还是只有订单号;
  • app_id、method、sign_type、sign 等字段是否完整;
  • 签名使用的私钥是否与对应的支付宝应用匹配;
  • 订单字符串是否已经过期。

如果接口返回 JSON,应读取其中的订单字符串字段,不要把整个 JSON 对象传给 payOrder:

// 正确:传递服务端返回的完整签名订单字符串
NSString *orderString = response[@"order_string"];

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

下面的处理可能导致签名失效:

// 不建议:客户端再次编码服务端已经生成的订单字符串
NSString *encodedOrder =
    [orderString stringByAddingPercentEncodingWithAllowedCharacters:
        [NSCharacterSet URLQueryAllowedCharacterSet]];

2. 核对 fromScheme 和 URL Types

fromScheme 用于支付宝完成支付后跳回商户 App,必须与 Xcode 中配置的 URL Scheme 完全一致。

在 Target 的 Info → URL Types → URL Schemes 中配置自定义 Scheme,例如:

merchantAppScheme

调用时使用相同的值:

[[AlipaySDK defaultService] payOrder:orderString
                         fromScheme:@"merchantAppScheme"
                           callback:^(NSDictionary *resultDic) {
    NSLog(@"pay callback: %@", resultDic);
}];

检查以下配置:

  • 不要填写 Universal Link;
  • 不要填写 Bundle Identifier,除非注册的 Scheme 本身就是这个值;
  • Scheme 中不要包含 ://;
  • 大小写和拼写必须一致;
  • 使用 Debug、Release、多 Target 或多环境构建时,确认实际安装包中的配置一致;
  • 不同商户 App 不要意外使用相同的回跳 Scheme。

还可以直接检查最终构建产物中的 Info.plist,确认 CFBundleURLTypes 包含目标 Scheme。

3. 正确处理支付宝回跳

调用 payOrder 后,还要处理支付宝通过 URL Scheme 跳回商户 App 的事件,并将 URL 传给支付宝 SDK。

使用 AppDelegate

#import <AlipaySDK/AlipaySDK.h>

- (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 standby result: %@", resultDic);
            }];
    }

    return YES;
}

如果项目仍需兼容旧的应用回调入口,也要确认该入口没有遗漏,且不会被其他 SDK 拦截:

- (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 standby result: %@", resultDic);
            }];
    }

    return YES;
}

使用 SceneDelegate

启用 Scene 生命周期后,回跳 URL 可能进入 SceneDelegate,而不是 AppDelegate:

#import <AlipaySDK/AlipaySDK.h>

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

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

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

如果应用同时接入了微信、登录、分享等多个 SDK,不要在某个处理器中无条件提前 return,否则支付宝回跳可能在中途被截断。

4. 在主线程调用支付接口

涉及应用跳转的操作建议放在主线程执行:

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

Swift 示例:

DispatchQueue.main.async {
    AlipaySDK.defaultService().payOrder(
        orderString,
        fromScheme: "merchantAppScheme"
    ) { result in
        print("Alipay result: \(String(describing: result))")
    }
}

调用支付时还要确认:

  • 当前 ViewController 没有执行异常转场;
  • 应用处于前台活动状态;
  • 没有连续重复调用 payOrder;
  • 点击事件没有被防抖、状态锁或业务判断提前拦截。

5. 记录并区分两类支付结果

支付结果可能通过两个入口返回:

  1. payOrder 的 callback;
  2. App 被支付宝拉起后,通过 processOrderWithPaymentResult 的 standbyCallback 返回。

两个入口都应记录完整结果:

NSLog(@"resultStatus: %@", resultDic[@"resultStatus"]);
NSLog(@"memo: %@", resultDic[@"memo"]);
NSLog(@"result: %@", resultDic[@"result"]);

排查时不要只看是否发生跳转,还要确认:

  • payOrder 回调是否执行;
  • URL 回跳入口是否执行;
  • processOrderWithPaymentResult 是否执行;
  • resultStatus、memo 和 result 的原始值;
  • 服务端是否收到异步通知;
  • 服务端查询到的最终订单状态。

客户端回调只能用于辅助展示结果。最终支付状态应以服务端异步通知或主动查询结果为准。

6. 检查 SDK 集成和应用查询配置

确认工程中没有同时引入多个来源或版本的支付宝 SDK,例如同时使用 CocoaPods、Swift Package 和手动 Framework。

还要检查:

  • SDK Framework 是否正确链接;
  • -ObjC 等链接参数是否按照当前 SDK 接入文档配置;
  • Release 包是否存在符号裁剪或依赖缺失;
  • 真机架构是否完整;
  • 支付宝 SDK 是否兼容当前 iOS、Xcode 和支付宝客户端;
  • Info.plist 中用于查询支付宝的 Scheme,是否按照所用 SDK 的官方接入文档配置到 LSApplicationQueriesSchemes。

示例结构如下。具体 Scheme 列表应以当前接入的 SDK 文档为准,不要凭经验随意添加:

<key>LSApplicationQueriesSchemes</key>
<array>
    <!-- 按当前支付宝 SDK 接入文档填写 -->
</array>

修改 Info.plist 后,应卸载旧 App 再重新安装测试,避免旧安装包的配置影响判断。

7. 用最小化测试定位问题

建议按照以下顺序排查:

  1. 让服务端生成一笔新订单,不要复用旧订单。
  2. 调用前记录订单字符串的长度和必要字段。
  3. 确认 fromScheme 与安装包中的 URL Scheme 完全一致。
  4. 确认 payOrder 在主线程执行。
  5. 在 AppDelegate 或 SceneDelegate 对应入口添加断点和日志。
  6. 记录支付宝 SDK 两类回调的完整字典。
  7. 检查服务端异步通知和订单查询结果。
  8. 换一台真机和另一个版本的支付宝客户端交叉验证。
  9. 使用同一个订单接口,分别测试已安装和未安装支付宝的情况。

可以根据现象缩小排查范围:

  • 完全没有打开支付宝:先检查支付调用是否执行、订单字符串、SDK 集成和客户端跳转能力。
  • 打开支付宝但没有进入收银台:先检查订单参数、签名、应用身份和订单有效期。
  • 完成操作后无法返回商户 App:先检查 fromScheme、URL Types 和生命周期回调。
  • 可以返回,但业务页面一直显示处理中:先检查结果解析、服务端通知和订单查询。
  • 只有 Release 包异常:先比较构建配置、环境参数、SDK 链接方式和实际的 Info.plist。

注意事项

Safari 能通过 Universal Link 打开商户 App,只能证明商户 App 的 Universal Link 配置基本正常,不能证明支付宝 SDK 的订单拉起和 URL Scheme 回跳配置正确。

未安装支付宝时 H5 支付正常,说明 H5 兜底链路大概率可用。不过,原生客户端支付使用的参数处理、SDK 跳转和回跳路径仍可能单独出现问题。

如果完成以上检查后仍无法定位,需要补充以下信息:

  • payOrder 的实际调用代码;
  • 脱敏后的订单字符串结构和长度;
  • fromScheme 的实际值;
  • Info.plist 中与 URL Types 相关的配置;
  • AppDelegate 或 SceneDelegate 的 URL 回调代码;
  • SDK 回调中的 resultStatus、memo 和 result;
  • iOS 版本、支付宝客户端版本和支付宝 SDK 版本;
  • 故障发生在 Debug、Release,还是两者都会发生。

提供订单字符串前,应对签名、用户标识和交易信息做脱敏处理。不要公开私钥、完整签名材料或真实交易数据。

备注:内容仅供参考。