/* import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/product.dart'; import 'package:aitrainer_app/service/logging.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; class PlatformPurchaseApi with Logging { static final PlatformPurchaseApi _singleton = PlatformPurchaseApi._internal(); bool _kAutoConsume = true; final String _kConsumableId = 'consumable'; final InAppPurchaseConnection _connection = InAppPurchaseConnection.instance; bool _isAvailable = false; List _products = []; List _purchases = []; List _notFoundIds = []; List _consumables = []; bool _purchasePending = false; String _queryProductError; StreamSubscription> _subscription; final List _productList = List(); List getProductList() => _productList; factory PlatformPurchaseApi() { return _singleton; } PlatformPurchaseApi._internal(); Future close() async { _subscription.cancel(); } Future initStoreInfo() async { final bool isAvailable = await _connection.isAvailable(); if (!isAvailable) { _isAvailable = isAvailable; _products = []; _purchases = []; _notFoundIds = []; _consumables = []; _purchasePending = false; log("Payment processor not available"); return; } getSubscriptions(); ProductDetailsResponse productDetailResponse = await _connection.queryProductDetails(_productList.toSet()); log("ProductDetailsResponse " + productDetailResponse.toString()); if (productDetailResponse.error != null) { _queryProductError = productDetailResponse.error.message; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; _consumables = []; _purchasePending = false; log("Payment processor not available"); return; } if (productDetailResponse.productDetails.isEmpty) { _queryProductError = null; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; _consumables = []; _purchasePending = false; return; } _products = productDetailResponse.productDetails; _products.forEach((element) { ProductDetails product = element as ProductDetails; log("Product " + product.id + " " + product.price + " " + product.skuDetail.toString()); }); final QueryPurchaseDetailsResponse purchaseResponse = await _connection.queryPastPurchases(); if (purchaseResponse.error != null) { // handle query past purchase error.. } final List verifiedPurchases = []; for (PurchaseDetails purchase in purchaseResponse.pastPurchases) { if (await _verifyPurchase(purchase)) { verifiedPurchases.add(purchase); } } //TODO List consumables = await ConsumableStore.load(); _isAvailable = isAvailable; _products = productDetailResponse.productDetails; _purchases = verifiedPurchases; _notFoundIds = productDetailResponse.notFoundIDs; //_consumables = consumables; _purchasePending = false; } // Platform messages are asynchronous, so we initialize in an async method. Future initPurchasePlatform() async { if (_productList.length > 0) { return; } log(' --- InappPurchase connection: $_connection'); log(" --- Init PurchasePlatform"); await this.initStoreInfo(); Stream purchaseUpdated = InAppPurchaseConnection.instance.purchaseUpdatedStream; _subscription = purchaseUpdated.listen((purchaseDetailsList) { _listenToPurchaseUpdated(purchaseDetailsList); }, onDone: () { _subscription.cancel(); }, onError: (error) { // handle error here. log("Error listen purchase updated " + error.toString()); }); } void deliverProduct(PurchaseDetails purchaseDetails) async { log("DeliverProduct"); // IMPORTANT!! Always verify a purchase purchase details before delivering the product. if (purchaseDetails.productID == _kConsumableId) { //TODO //await ConsumableStore.save(purchaseDetails.purchaseID); //List consumables = await ConsumableStore.load(); _purchasePending = false; //_consumables = consumables; } else { _purchases.add(purchaseDetails); _purchasePending = false; } } Future _verifyPurchase(PurchaseDetails purchaseDetails) { log("verifyPurchase"); // IMPORTANT!! Always verify a purchase before delivering the product. // For the purpose of an example, we directly return true. log("Verifying Purchase" + purchaseDetails.toString()); return Future.value(true); } void _handleInvalidPurchase(PurchaseDetails purchaseDetails) { // handle invalid purchase here if _verifyPurchase` failed. log("Invalid Purchase" + purchaseDetails.toString()); } void _listenToPurchaseUpdated(List purchaseDetailsList) { purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async { if (purchaseDetails.status == PurchaseStatus.pending) { //showPendingUI(); log("Purchase pending"); } else { if (purchaseDetails.status == PurchaseStatus.error) { //handleError(purchaseDetails.error); } else if (purchaseDetails.status == PurchaseStatus.purchased) { bool valid = await _verifyPurchase(purchaseDetails); if (valid) { deliverProduct(purchaseDetails); } else { _handleInvalidPurchase(purchaseDetails); return; } } if (Platform.isAndroid) { if (!_kAutoConsume && purchaseDetails.productID == _kConsumableId) { await InAppPurchaseConnection.instance.consumePurchase(purchaseDetails); } } if (purchaseDetails.pendingCompletePurchase) { await InAppPurchaseConnection.instance.completePurchase(purchaseDetails); } } }); } void getSubscriptions() { Cache().products.forEach((element) { Product product = element as Product; if (Platform.isAndroid) { if (product.productIdAndroid != null && product.productIdAndroid.isNotEmpty) { _productList.add(product.productIdAndroid); } } else { if (product.productIdIos != null && product.productIdIos.isNotEmpty) { _productList.add(product.productIdIos); } } }); _productList.forEach((element) { print(element); }); } void purchase(Product product) { if (product == null) { throw Exception("No product to purchase"); } String productId = Platform.isAndroid ? product.productIdAndroid : product.productIdIos; ProductDetails productDetails; _products.forEach((element) { if (element.id == productId) { productDetails = element; log("product to purchase: " + productDetails.id + " title " + productDetails.title + " price " + productDetails.price + " period " + productDetails.skuDetail.subscriptionPeriod); } }); final PurchaseParam purchaseParam = PurchaseParam(productDetails: productDetails); /* if (_isConsumable(productDetails)) { InAppPurchaseConnection.instance.buyConsumable(purchaseParam: purchaseParam); } else { */ InAppPurchaseConnection.instance.buyNonConsumable(purchaseParam: purchaseParam); //} // From here the purchase flow will be handled by the underlying storefront. // Updates will be delivered to the `InAppPurchaseConnection.instance.purchaseUpdatedStream`. } } */