PAYATHON 2026

iOS SiriKit 中 handle 与 confirm delegates 未调用

支付阿杰

结论

只有在意图解析完成,并且 confirmSendPayment:completion: 返回 INSendPaymentIntentResponseCodeReady 后,系统才会调用 handleSendPayment:completion:。

如果 Siri 提示“需要在 App 中继续”,通常是因为确认阶段返回了 FailureRequiringAppLaunch,或者 Intent Extension 出现超时、崩溃、配置错误、方法签名不正确等问题。排查时,应重点查看确认回调实际传给 Siri 的 responseCode,不能只确认方法是否被调用。

正常调用顺序

支付 Intent 通常按以下顺序执行:

  1. resolvePayeeForSendPayment:withCompletion:
  2. resolveCurrencyAmountForSendPayment:withCompletion:
  3. confirmSendPayment:completion:
  4. handleSendPayment:completion:

所有参数解析成功,并且确认阶段返回 Ready 后,Siri 才会调用 handleSendPayment。

正确的 Objective-C 实现

#import <Intents/Intents.h>

@interface IntentHandler () <INSendPaymentIntentHandling>
@end

@implementation IntentHandler

- (id)handlerForIntent:(INIntent *)intent
{
    if ([intent isKindOfClass:[INSendPaymentIntent class]]) {
        return self;
    }

    return self;
}

- (void)resolvePayeeForSendPayment:(INSendPaymentIntent *)intent
                    withCompletion:
    (void (^)(INPersonResolutionResult *resolutionResult))completion
{
    INPerson *payee = intent.payee;

    if (payee == nil) {
        completion([INPersonResolutionResult needsValue]);
        return;
    }

    // 实际项目中应根据联系人、账户或服务端数据确认收款人。
    completion([INPersonResolutionResult successWithResolvedPerson:payee]);
}

- (void)resolveCurrencyAmountForSendPayment:(INSendPaymentIntent *)intent
                             withCompletion:
    (void (^)(INCurrencyAmountResolutionResult *resolutionResult))completion
{
    INCurrencyAmount *amount = intent.currencyAmount;

    if (amount == nil || amount.amount == nil) {
        completion([INCurrencyAmountResolutionResult needsValue]);
        return;
    }

    completion(
        [INCurrencyAmountResolutionResult
            successWithResolvedCurrencyAmount:amount]
    );
}

- (void)confirmSendPayment:(INSendPaymentIntent *)intent
                completion:
    (void (^)(INSendPaymentIntentResponse *response))completion
{
    // 此处只检查支付是否具备执行条件,不要返回 Success。
    BOOL paymentCanProceed = YES;

    INSendPaymentIntentResponseCode code =
        paymentCanProceed
            ? INSendPaymentIntentResponseCodeReady
            : INSendPaymentIntentResponseCodeFailure;

    INSendPaymentIntentResponse *response =
        [[INSendPaymentIntentResponse alloc] initWithCode:code
                                             userActivity:nil];

    completion(response);
}

- (void)handleSendPayment:(INSendPaymentIntent *)intent
               completion:
    (void (^)(INSendPaymentIntentResponse *response))completion
{
    // 在这里执行真正的支付操作。
    BOOL paymentSucceeded = YES;

    INSendPaymentIntentResponseCode code =
        paymentSucceeded
            ? INSendPaymentIntentResponseCodeSuccess
            : INSendPaymentIntentResponseCodeFailure;

    INSendPaymentIntentResponse *response =
        [[INSendPaymentIntentResponse alloc] initWithCode:code
                                             userActivity:nil];

    completion(response);
}

@end

其中最重要的是:

INSendPaymentIntentResponseCodeReady

如果确认阶段返回其他状态,尤其是:

INSendPaymentIntentResponseCodeFailureRequiringAppLaunch

系统会要求用户打开主 App,不再调用 handleSendPayment:completion:。

检查 Intent Extension 配置

确认 Intent Extension 的 Info.plist 包含支付 Intent:

<key>NSExtension</key>
<dict>
    <key>NSExtensionAttributes</key>
    <dict>
        <key>IntentsSupported</key>
        <array>
            <string>INSendPaymentIntent</string>
        </array>
    </dict>
    <key>NSExtensionPointIdentifier</key>
    <string>com.apple.intents-service</string>
    <key>NSExtensionPrincipalClass</key>
    <string>$(PRODUCT_MODULE_NAME).IntentHandler</string>
</dict>

在 Objective-C 项目中,NSExtensionPrincipalClass 也可以直接填写类名,具体写法取决于 Extension Target 的模块配置。无论采用哪种写法,系统都必须能实例化实际实现了 INSendPaymentIntentHandling 的类。

还需要逐项检查:

  • handlerForIntent: 返回的对象是否实现了 INSendPaymentIntentHandling。
  • INSendPaymentIntent 是否已经加入 IntentsSupported。
  • App 和 Intent Extension 的 Deployment Target 是否都兼容 iOS 10。
  • 方法签名是否与 SDK 声明完全一致,不要自行修改 selector。
  • 每个 completion block 是否只调用一次,并且在所有分支中都会调用。
  • Intent Extension 是否在异步操作完成前退出、超时或崩溃。

如果确认方法也没有调用

问题描述中同时提到了“confirmSendPayment 能调用”和“confirmSendPayment 未调用”,这两种情况需要分开排查。

如果解析方法执行后,confirmSendPayment:completion: 始终没有进入,应先检查解析结果。返回以下任一结果时,系统都不会直接进入确认阶段:

[INPersonResolutionResult needsValue]
[INPersonResolutionResult unsupported]
[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:...]

金额解析遵循同样的规则。所有参数最终都返回成功结果后,系统才会继续执行:

[INPersonResolutionResult successWithResolvedPerson:payee]

[INCurrencyAmountResolutionResult
    successWithResolvedCurrencyAmount:amount]

还应在 Extension 的所有入口处设置断点或日志,检查是否有分支遗漏 completion、发生异常退出,或者方法签名不匹配。

注意事项

confirmSendPayment 只用于检查支付条件,例如账户状态、余额、币种和收款人是否有效。真正的支付操作应放在 handleSendPayment 中。不要在确认阶段完成扣款,因为 Siri 可能重复确认,也可能中止流程。

如果业务要求用户进入主 App 完成额外的身份验证,可以返回 INSendPaymentIntentResponseCodeFailureRequiringAppLaunch。此时 Siri 提示用户在 App 中继续,属于预期行为。如果希望整个支付流程都在 Siri 中完成,确认阶段必须返回 Ready,同时确保 Extension 能在系统规定的时间内完成处理。

备注:内容仅供参考。