You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android端调用Stripe.createPaymentMethod致应用卡顿并终止执行

Flutter Stripe卡片保存:卡顿与Isolate使用问题

问题描述

使用flutter_stripe实现应用内卡片保存功能,流程如下:

  1. 调用Stripe.instance.createPaymentMethod
  2. 调用Stripe.instance.createToken
  3. 将Token提交至RestAPI

Android端出现两个核心问题:

  • 步骤1执行耗时久,引发主线程卡顿(日志显示跳过73帧)
  • 步骤1完成后,后续步骤因类型转换错误终止执行;尝试用Isolate优化性能时,又遇到配置相关的异常

代码片段

步骤1:创建PaymentMethod

await Stripe.instance.createPaymentMethod(
  params: PaymentMethodParams.card(
    paymentMethodData: PaymentMethodData(
      billingDetails: BillingDetails(name: cardHolderName), // 备注:此参数未生效
    ),
  ),
  options: const PaymentMethodOptions(
      setupFutureUsage: PaymentIntentsFutureUsage.OnSession));

步骤2-3:创建Token并提交API

await Stripe.instance
    .createToken(const CreateTokenParams.card(
        params: CardTokenParams(
  type: TokenType.Card,

     ))).then((value) async {

  print('Token Received');

  // 调用API提交Token
  await createCard(value.id);

}).onError((error, stackTrace) {
 getErrorDialog(error);
});

报错日志

执行日志(卡顿+类型转换错误)

I/flutter (18568): Started Execution
I/ViewRootImpl(18568): jank_removeInvalidNode jank list is null
...(重复的jank日志)
I/flutter (18568): Pay Method Created
I/Choreographer(18568): Skipped 73 frames!  The application may be doing too much work on its main thread.
...
E/flutter ( 1627): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'int' is not a subtype of type 'String' in type cast
E/flutter ( 1627): #0      _$$_TokenDataFromJson (package:stripe_platform_interface/src/models/create_token_data.g.dart:146:40)
E/flutter ( 1627): #1      new _$_TokenData.fromJson (package:stripe_platform_interface/src/models/create_token_data.freezed.dart:1777:7)
...(调用栈省略)

使用compute时的报错

Unhandled Exception: Instance of 'StripeConfigException'

重新设置publishKey后的报错

Unhandled Exception: Bad state: The BackgroundIsolateBinaryMessenger.instance value is invalid until BackgroundIsolateBinaryMessenger.ensureInitialized is executed.
E/flutter (29872): #0   BackgroundIsolateBinaryMessenger.instance (package:flutter/src/services/_background_isolate_binary_messenger_io.dart:27:7)
...

解决方案

1. 修复类型转换错误(优先解决)

日志中的类型转换错误是因为createToken的参数不完整,导致Stripe返回的Token数据解析失败。若使用CardField组件获取卡片信息,需从组件状态中提取完整卡片详情后再调用createToken:

// 假设cardField是CardField组件的GlobalKey
final cardFieldState = cardField.currentState;
final cardDetails = await cardFieldState?.completeCardDetails();

if (cardDetails != null) {
  final tokenResult = await Stripe.instance.createToken(
    CreateTokenParams.card(
      params: CardTokenParams(
        number: cardDetails.number,
        expMonth: cardDetails.expMonth,
        expYear: cardDetails.expYear,
        cvc: cardDetails.cvc,
      ),
    ),
  );
  // 提交Token到API
  await createCard(tokenResult.id);
}

2. 解决主线程卡顿问题

flutter_stripe的异步方法理论上不会阻塞主线程,但Android端底层可能存在同步操作。可以通过以下方式优化:

  • 显示加载动画:在执行步骤1时展示加载状态,避免用户感知卡顿
  • 避免不必要的操作:检查是否有其他同步计算或UI操作与步骤1并行执行
  • 正确使用Isolate(仅当必须时):若必须将Stripe操作移到后台Isolate,需先初始化BackgroundIsolateBinaryMessenger,并重新配置Stripe:
import 'dart:isolate';
import 'package:flutter/services.dart';
import 'package:flutter_stripe/flutter_stripe.dart';

Future<void> runStripeInBackground(String publishKey, String cardHolderName, CardDetails cardDetails) async {
  final result = await Isolate.run(() async {
    // 初始化后台Isolate的Messenger
    BackgroundIsolateBinaryMessenger.ensureInitialized();
    
    // 重新配置Stripe
    Stripe.publishableKey = publishKey;
    await Stripe.instance.applySettings();
    
    // 执行PaymentMethod创建
    final paymentMethod = await Stripe.instance.createPaymentMethod(
      params: PaymentMethodParams.card(
        paymentMethodData: PaymentMethodData(
          billingDetails: BillingDetails(name: cardHolderName),
        ),
      ),
      options: const PaymentMethodOptions(
        setupFutureUsage: PaymentIntentsFutureUsage.OnSession,
      ),
    );
    
    // 创建Token
    final tokenResult = await Stripe.instance.createToken(
      CreateTokenParams.card(
        params: CardTokenParams(
          number: cardDetails.number,
          expMonth: cardDetails.expMonth,
          expYear: cardDetails.expYear,
          cvc: cardDetails.cvc,
        ),
      ),
    );
    
    return {'paymentMethodId': paymentMethod.id, 'tokenId': tokenResult.id};
  });
  
  // 在主线程处理结果(如提交API、更新UI)
  await createCard(result['tokenId']);
}

注意:Isolate无法直接访问UI组件状态,需将卡片详情等数据作为参数传递。

3. 优化流程(推荐)

Stripe官方推荐使用SetupIntent保存卡片,无需手动创建PaymentMethod和Token,流程更高效且减少前端操作:

  1. 后端创建SetupIntent并返回clientSecret
  2. 前端调用confirmSetupIntent完成卡片保存:
await Stripe.instance.confirmSetupIntent(
  clientSecret,
  params: SetupIntentConfirmParams.card(
    billingDetails: BillingDetails(name: cardHolderName),
  ),
);
  1. 后端通过SetupIntent事件获取保存的PaymentMethod,无需前端传递Token

内容的提问来源于stack exchange,提问作者ASAD HAMEED

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 12:07:09