127 lines
4.3 KiB
Dart
127 lines
4.3 KiB
Dart
import 'dart:async';
|
||
import 'dart:io';
|
||
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:in_app_purchase/in_app_purchase.dart';
|
||
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
|
||
import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart';
|
||
import 'package:in_app_purchase_storekit/store_kit_wrappers.dart';
|
||
|
||
// 内购工具类
|
||
class KtInAppPurchaseUtil {
|
||
static final InAppPurchase _iap = InAppPurchase.instance;
|
||
|
||
/// 查询可用商品
|
||
static Future<ProductDetailsResponse> queryProducts(Set<String> productIds) async {
|
||
return await _iap.queryProductDetails(productIds);
|
||
}
|
||
|
||
/// 购买商品(自动区分消耗型/非消耗型)
|
||
static Future<void> buy(ProductDetails product, {bool consumable = false}) async {
|
||
final purchaseParam = PurchaseParam(productDetails: product);
|
||
if (consumable) {
|
||
await _iap.buyConsumable(purchaseParam: purchaseParam, autoConsume: Platform.isIOS);
|
||
} else {
|
||
await _iap.buyNonConsumable(purchaseParam: purchaseParam);
|
||
}
|
||
}
|
||
|
||
/// 监听购买更新
|
||
static Stream<List<PurchaseDetails>> get purchaseStream => _iap.purchaseStream;
|
||
|
||
/// 完成购买(消耗型商品)
|
||
static Future<void> completePurchase(PurchaseDetails purchase, {bool isRetry = false}) async {
|
||
if (purchase.pendingCompletePurchase) {
|
||
if (isRetry) {
|
||
for (int i = 0; i < 3; i++) {
|
||
try {
|
||
await _iap.completePurchase(purchase);
|
||
return;
|
||
} catch (e) {
|
||
debugPrint('---err:${e}');
|
||
}
|
||
}
|
||
} else {
|
||
await _iap.completePurchase(purchase);
|
||
}
|
||
}
|
||
|
||
// if (Platform.isAndroid) {
|
||
// final InAppPurchaseAndroidPlatformAddition addition =
|
||
// InAppPurchasePlatformAddition.instance! as InAppPurchaseAndroidPlatformAddition;
|
||
// if (purchase.productID.contains('coins')) {
|
||
// await addition.consumePurchase(purchase);
|
||
// }
|
||
// QueryPurchaseDetailsResponse response = await addition.queryPastPurchases();
|
||
// for (GooglePlayPurchaseDetails purchaseDetails in response.pastPurchases) {
|
||
// _iap.completePurchase(purchaseDetails);
|
||
// }
|
||
// } else {
|
||
// if (purchase.pendingCompletePurchase) {
|
||
// await _iap.completePurchase(purchase);
|
||
// }
|
||
// }
|
||
}
|
||
|
||
static Future<void> consumeIfNeeded(PurchaseDetails purchaseDetails) async {
|
||
if (purchaseDetails is GooglePlayPurchaseDetails) {
|
||
final InAppPurchaseAndroidPlatformAddition addition =
|
||
InAppPurchasePlatformAddition.instance! as InAppPurchaseAndroidPlatformAddition;
|
||
await addition.consumePurchase(purchaseDetails);
|
||
}
|
||
}
|
||
|
||
/// 检查是否可用
|
||
static Future<bool> isAvailable() async {
|
||
return await _iap.isAvailable();
|
||
}
|
||
|
||
/// 恢复购买(非消耗型商品)
|
||
static Future<void> restorePurchases() async {
|
||
await _iap.restorePurchases();
|
||
}
|
||
|
||
/// 校验购买状态
|
||
static bool isPurchaseSuccess(PurchaseDetails purchase) {
|
||
return purchase.status == PurchaseStatus.purchased || purchase.status == PurchaseStatus.restored;
|
||
}
|
||
|
||
/// 取消订阅流(如需手动管理)
|
||
static void cancelStreamSubscription(StreamSubscription? sub) {
|
||
sub?.cancel();
|
||
}
|
||
|
||
/// 完成错误的购买
|
||
// static void completeFailed() {
|
||
// _iap.clearPendingPurchases();
|
||
// }
|
||
|
||
// @visibleForTesting
|
||
static Future<void> clearFailedPurchases() async {
|
||
if (Platform.isIOS || Platform.isMacOS) {
|
||
final wrapper = SKPaymentQueueWrapper();
|
||
final transactions = await wrapper.transactions();
|
||
for (final transaction in transactions) {
|
||
// if (transaction.transactionState == SKPaymentTransactionStateWrapper.failed) {
|
||
await wrapper.finishTransaction(transaction);
|
||
// }
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 获取未完成的购买(通过监听 purchaseStream)
|
||
static Future<List<PurchaseDetails>> getPendingPurchases() async {
|
||
final available = await isAvailable();
|
||
if (!available) return [];
|
||
final List<PurchaseDetails> pending = [];
|
||
// 只获取一次当前流数据
|
||
final completer = Completer<List<PurchaseDetails>>();
|
||
final sub = purchaseStream.listen((purchases) {
|
||
pending.addAll(purchases.where((p) => p.status == PurchaseStatus.pending));
|
||
completer.complete(pending);
|
||
});
|
||
final result = await completer.future;
|
||
await sub.cancel();
|
||
return result;
|
||
}
|
||
} |