import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:webview_flutter/webview_flutter.dart'; import '../../di/injector.dart'; import '../../di/locator.dart'; import '../../flags.dart'; import '../../main.dart'; import '../analytics/analytics.dart'; import 'rustore/mnemo_rustore.dart'; enum StoreItemType { ads, subscription, pack, } class InAppPurchaseService { final InAppPurchase _inAppPurchase = InAppPurchase.instance; final Stream> storeSubscription = InAppPurchase.instance.purchaseStream; InAppPurchase get instance => _inAppPurchase; // Future buySubscription(SubscriptionPlanDto dto) async { // if (dto.paymentSystem == PaymentSystem.yookassa) { // return false; // } // // final subscriptionProducts = await _getSubscriptionStoreProduct(dto); // ProductDetails? product; // if (Platform.isAndroid && !RUSTORE_BUILD) { // // final googlePlayProducts = subscriptionProducts.whereType(); // product = subscriptionProducts.firstWhereOrNull( // (p) => p.rawPrice == double.parse(dto.price), // ); // } else { // product = subscriptionProducts.first; // } // if (product == null) { // Analytics.buySubscriptionError( // dto, // androidPaymentSystem, // message: 'cant find plan with price ${dto.price}', // data: { // 'plans': subscriptionProducts.join(';'), // }, // ); // return false; // } // final result = await _buySubscriptionInStore(product); // return result; // } Future buyPack(CardPackBuyDto dto) async { try { if (RUSTORE_BUILD) { // return RustorePurchaseService().buyPack(dto); return false; } else { final pack = await _getPackStoreProduct(dto); final result = await _buyItemInStore(pack.first); return result; } } catch (e, s) { log('err', error: e, stackTrace: s); Analytics.buyError( dto, androidPaymentSystem, data: { 'e': e.toString(), 's': s.toString(), }, ); return false; } } Future> _getPackStoreProduct(CardPackBuyDto dto) async { final bool isAvailable = await _inAppPurchase.isAvailable(); if (!isAvailable) { Analytics.buyError( dto, androidPaymentSystem, message: 'in app purchase not available', ); return []; } final ids = { if (Platform.isAndroid) RUSTORE_BUILD ? dto.rustoreId : dto.googlePlayId, if (Platform.isIOS) dto.appStoreId, }.whereNotNull().toSet(); if (ids.isEmpty) { Analytics.buyError( dto, androidPaymentSystem, message: 'product id not set', ); return []; } final ProductDetailsResponse productDetailResponse = await _inAppPurchase.queryProductDetails(ids); if (productDetailResponse.error != null || productDetailResponse.productDetails.isEmpty) { Analytics.buyError( dto, androidPaymentSystem, data: { 'msg': 'cant get product details ${productDetailResponse.error}', 'not_found': productDetailResponse.notFoundIDs.join(','), }, ); return []; } return productDetailResponse.productDetails; } // Future> _getSubscriptionStoreProduct( // SubscriptionPlanDto dto, // ) async { // final bool isAvailable = await _inAppPurchase.isAvailable(); // if (!isAvailable) { // Analytics.buySubscriptionError( // dto, // androidPaymentSystem, // message: 'in app purchase not available', // ); // return []; // } // final ids = { // if (Platform.isAndroid) // if (RUSTORE_BUILD && dto.paymentSystem == PaymentSystem.rustore) // dto.paymentId, // if (!RUSTORE_BUILD && dto.paymentSystem == PaymentSystem.google) // dto.googlePlaySubscriptionId, // if (dto.paymentSystem == PaymentSystem.yookassa) dto.paymentId, // if (Platform.isIOS && dto.paymentSystem == PaymentSystem.apple) // dto.paymentId, // }.whereNotNull().toSet(); // if (ids.isEmpty) { // Analytics.buySubscriptionError( // dto, // androidPaymentSystem, // message: 'product id not set', // ); // return []; // } // // final productDetailResponse = await _inAppPurchase.queryProductDetails(ids); // if (productDetailResponse.error != null || // productDetailResponse.productDetails.isEmpty) { // Analytics.buySubscriptionError( // dto, // androidPaymentSystem, // data: { // 'msg': 'cant get product details ${productDetailResponse.error}', // 'not_found': productDetailResponse.notFoundIDs.join(','), // }, // ); // return []; // } // // return productDetailResponse.productDetails; // } Future _buyItemInStore(ProductDetails product) async { final PurchaseParam purchaseParam = PurchaseParam(productDetails: product); return InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam); } Future _buySubscriptionInStore(ProductDetails product) async { final PurchaseParam purchaseParam = PurchaseParam(productDetails: product); return InAppPurchase.instance .buyNonConsumable(purchaseParam: purchaseParam); } Future completePurchase(PurchaseDetails purchaseDetails) async { await InAppPurchase.instance.completePurchase(purchaseDetails); } } class PurchaseDetailsStreamSubscription { final InAppPurchaseService inAppPurchaseService = getIt.get(); Future onPurchased(PurchaseDetails purchaseDetails) async { try { Analytics.buyEvent( null, androidPaymentSystem, message: 'onPurchased', data: { 'status': purchaseDetails.status.name, 'product': purchaseDetails.productID, 'date': purchaseDetails.transactionDate, }, ); final result = await locator.repository.checkPayment( product: MnemoCardsProduct( id: purchaseDetails.productID, type: MnemoCardsProductType.unknown, ), key: purchaseDetails.verificationData.serverVerificationData, paymentSystem: androidPaymentSystem, ); if (result) { Analytics.buyComplete( null, androidPaymentSystem, message: purchaseDetails.productID, ); log('Purchased'); } else { Analytics.buyError( null, PaymentSystem.yookassa, message: 'payment not verified ${purchaseDetails.productID}', ); throw Exception('Cant verify purchase'); } } catch (e, s) { log(e.toString(), stackTrace: s); } } StreamSubscription>? _streamSubscription; PurchaseDetailsStreamSubscription(); Future init() async { _streamSubscription = inAppPurchaseService.storeSubscription.listen( (List events) { Future.forEach( events, (PurchaseDetails purchaseDetails) async { if (purchaseDetails.pendingCompletePurchase || purchaseDetails.status == PurchaseStatus.purchased) { onPurchased(purchaseDetails); } }, ); }, ); } void close() { _streamSubscription?.cancel(); } }