257 lines
7.7 KiB
Dart
257 lines
7.7 KiB
Dart
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<List<PurchaseDetails>> storeSubscription =
|
|
InAppPurchase.instance.purchaseStream;
|
|
|
|
InAppPurchase get instance => _inAppPurchase;
|
|
|
|
// Future<bool> 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<GooglePlayProductDetails>();
|
|
// 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<bool> 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<List<ProductDetails>> _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<List<ProductDetails>> _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<bool> _buyItemInStore(ProductDetails product) async {
|
|
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
|
|
return InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
|
|
}
|
|
|
|
Future<bool> _buySubscriptionInStore(ProductDetails product) async {
|
|
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
|
|
return InAppPurchase.instance
|
|
.buyNonConsumable(purchaseParam: purchaseParam);
|
|
}
|
|
|
|
Future<void> completePurchase(PurchaseDetails purchaseDetails) async {
|
|
await InAppPurchase.instance.completePurchase(purchaseDetails);
|
|
}
|
|
}
|
|
|
|
class PurchaseDetailsStreamSubscription {
|
|
final InAppPurchaseService inAppPurchaseService = getIt.get();
|
|
|
|
Future<void> 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<List<PurchaseDetails>>? _streamSubscription;
|
|
|
|
PurchaseDetailsStreamSubscription();
|
|
|
|
Future<void> init() async {
|
|
_streamSubscription = inAppPurchaseService.storeSubscription.listen(
|
|
(List<PurchaseDetails> events) {
|
|
Future.forEach(
|
|
events,
|
|
(PurchaseDetails purchaseDetails) async {
|
|
if (purchaseDetails.pendingCompletePurchase ||
|
|
purchaseDetails.status == PurchaseStatus.purchased) {
|
|
onPurchased(purchaseDetails);
|
|
}
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
void close() {
|
|
_streamSubscription?.cancel();
|
|
}
|
|
}
|