74 lines
2.1 KiB
Dart
74 lines
2.1 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
|
|
|
import 'ad_config.dart';
|
|
|
|
/// Wraps the non-consumable `remove_ads` purchase. On any verified purchase
|
|
/// or restore of the product it invokes [onEntitlementGranted]; the caller
|
|
/// flips the AdsRemovedNotifier. All failures are swallowed.
|
|
class IapService {
|
|
IapService({required this.onEntitlementGranted});
|
|
|
|
final Future<void> Function() onEntitlementGranted;
|
|
final InAppPurchase _iap = InAppPurchase.instance;
|
|
StreamSubscription<List<PurchaseDetails>>? _sub;
|
|
ProductDetails? _product;
|
|
|
|
bool get available => _available;
|
|
bool _available = false;
|
|
|
|
ProductDetails? get product => _product;
|
|
|
|
Future<void> initialize() async {
|
|
try {
|
|
_available = await _iap.isAvailable();
|
|
if (!_available) return;
|
|
_sub = _iap.purchaseStream.listen(_onPurchases,
|
|
onError: (_) {}, cancelOnError: false);
|
|
final response =
|
|
await _iap.queryProductDetails({AdConfig.removeAdsProductId});
|
|
if (response.productDetails.isNotEmpty) {
|
|
_product = response.productDetails.first;
|
|
}
|
|
} catch (e) {
|
|
debugPrint('IAP init failed: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> buyRemoveAds() async {
|
|
final product = _product;
|
|
if (product == null) return;
|
|
try {
|
|
await _iap.buyNonConsumable(
|
|
purchaseParam: PurchaseParam(productDetails: product));
|
|
} catch (e) {
|
|
debugPrint('IAP buy failed: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> restorePurchases() async {
|
|
try {
|
|
await _iap.restorePurchases();
|
|
} catch (e) {
|
|
debugPrint('IAP restore failed: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> _onPurchases(List<PurchaseDetails> purchases) async {
|
|
for (final p in purchases) {
|
|
if (p.productID != AdConfig.removeAdsProductId) continue;
|
|
if (p.status == PurchaseStatus.purchased ||
|
|
p.status == PurchaseStatus.restored) {
|
|
await onEntitlementGranted();
|
|
}
|
|
if (p.pendingCompletePurchase) {
|
|
await _iap.completePurchase(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void dispose() => _sub?.cancel();
|
|
}
|