我正在为 iOS 上的应用内支付而苦苦挣扎。当我尝试启动应用程序时,我正在恢复过去的购买,有时我会收到代码为 0 的 SKError(未知错误)。 我无法获得可用产品和购买清单。 不知道是什么原因导致错误,我为此苦苦挣扎了几天。
重要的是,有时它能正常工作,有时却不能。 Android 版本运行完美。
Xcode:14.2 颤动版本:3.7.6 飞镖版本:2.19.3 我使用的包和版本:in_app_purchase: ^3.1.5
出现问题的设备: iPhone Xs MAX,iOS 16.3.1 iPhone 6s,iOS 15.7.3 iPad 空气 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 中的 bloc 声明之后调用,如下所示:
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()),
);
}
}