Flutter in_app_purchase couse unknown SKError on iOS

92dk7w1h  于 2023-03-24  发布在  Flutter
关注(0)|答案(1)|浏览(113)

bounty将在6天后过期。回答此问题可获得+50声望奖励。JBTG希望引起更多人关注此问题。

我正在努力在iOS上的应用程序支付.当我尝试启动应用程序,我恢复过去的购买,有时我得到代码0 SKError(未知错误).我无法获得可用的产品,和购买列表.不知道是什么原因导致错误,我挣扎了几天.
重要的是,有时它工作正常,有时它只是不. Android版本是完美的工作.
Xcode:14.2 Flutter版本:3.7.6 Dart版本:2.19.3我使用的包和版本:in_app_purchase:第三年第一年第五次
出现问题的设备:iPhone Xs MAX,iOS 16.3.1 iPhone 6s,iOS 15.7.3 iPad Air 2,iOS 15.7.3
下面是我在Cubit中的代码:

import 'dart:async';
import 'dart:io';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:in_app_purchase/in_app_purchase.dart';

part 'payments_state.dart';

class PaymentsCubit extends Cubit<PaymentsState> {
  PaymentsCubit()
      : super(PaymentsState(
          isPremium: null,
          avaliable: false,
          products: [],
          purchases: [],
        ));
  late StreamSubscription subscription;
  late InAppPurchase iap;

  void start() async {
    iap = InAppPurchase.instance;

    state.avaliable = await iap.isAvailable();
    if (state.avaliable) {
      //listener must be declared before get products and purchases calls
      subscription = iap.purchaseStream.listen(
        (purchaseDetailsList) {
          listenToPurchaseUpdated(purchaseDetailsList);
        },
        onDone: () => subscription.cancel(),
        onError: (error) => print(error),
      );
      await getProducts();
      await getPastPurchases();
      //it's listening to every changes in purchases e.g. buy new product, restore old products.
      //everytime when change will occur, then function is triggered
      //remember to always cancel listeners to avoid memmory leaks
    }
    emit(state);
  }

  Future<void> getProducts() async {
    Set<String> ids = Platform.isAndroid
        ? {
            'com.example.example.example',
            'com.example.example.exampleNew'
          }
        : {
            'com.example.example.exampleNew',
          };
    ProductDetailsResponse response = await iap.queryProductDetails(ids);
    state.products = response.productDetails;
  }

  Future<void> getPastPurchases() async {
    //this will trigger listener if user made purchases in the past
    await iap.restorePurchases();
  }

  void buyProduct() async {
    state.avaliable = await iap.isAvailable();
    if (state.avaliable) {
      final PurchaseParam purchaseParam = PurchaseParam(
        productDetails: state.products.firstWhere(
          (element) =>
              element.id ==
              (Platform.isAndroid
                  ? 'com.example.example.example'
                  : 'com.example.example.exampleNew'),
        ),
      );
      await InAppPurchase.instance
          .buyNonConsumable(purchaseParam: purchaseParam);
    }
  }

  void listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
    purchaseDetailsList.forEach((element) {
      if (element.status == PurchaseStatus.pending) {
        //handle situation when user pressed button to start purchase, (on iOS it takes more time, android have almost immediately response) maybe show some loading spinners
      } else if (element.status == PurchaseStatus.error) {
      } else if (element.status == PurchaseStatus.canceled) {
      } else if (element.status == PurchaseStatus.purchased ||
          element.status == PurchaseStatus.restored) {
        state.purchases.add(element);
        emit(state.copyWith(
          isPremium: true,
        ));
        iap.completePurchase(element);
      }
    });
    if (state.isPremium == null) {
      emit(state.copyWith(isPremium: false));
    }
  }
}

Start方法在MyApp中的块声明后调用,如下所示:

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(create: (context) => PaymentsCubit()..start()),
      ],
      child: MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            fontFamily: 'Rubik',
            primarySwatch: Colors.blue,
          ),
          home: const FakeSplashscreen()),
    );
  }
}
m0rkklqb

m0rkklqb1#

代码看起来很好。
发生SKError Code 0时,您可能需要检查错误对象的localizedDescription属性。
当测试过程中出现此错误时,通常可以通过注销iTunes和/或App Store并在App Store Connect中创建新的测试用户帐户来解决。这种方法仅在设备上运行应用时有效,而不是模拟器。
当生产中出现此错误时,可能表示用户的iTunes帐户有问题。

相关问题