Soo, below is my implementation of Flutter In-app purchases on both ios and android.
For this, I've used flutter_inapp_purchase 5.1.2 package.
The main problem is when I try to get an active subscription on ios. What I mean is that I have no clue how to check exactly whether the user is still subscribed or not. I tested my app 2 months ago and getAvailablePurcheses provides me with a purchase that has purchaseDate set to 2 months ago.
How can I check if the user is still subscribed and how can I get my subscription expiration date in order to show it to the user?
Please help, I hope this class will be useful to other users later.
import 'dart:async';
import 'package:estimator_ar/models/user.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
import 'dart:io' show Platform;
class PaymentService {
/// We want singelton object of ``PaymentService`` so create private constructor
PaymentService._internal();
static final PaymentService instance = PaymentService._internal();
StreamSubscription<ConnectionResult> _connectionSubscription;
StreamSubscription<PurchasedItem> _purchaseUpdatedSubscription;
/// To listen the errors of the purchase
StreamSubscription<PurchaseResult> _purchaseErrorSubscription;
final String _productId = 'estimator_tier_1';
IAPItem _product;
ObserverList<Function> _subscriptionStatusChangedListeners = new ObserverList<Function>();
ObserverList<Function(String)> _errorListeners =new ObserverList<Function(String)>();
/// Call this method at the startup of you app to initialize connection
/// with billing server and get all the necessary data
Future<void> initConnection() async {
await FlutterInappPurchase.instance.initialize();
_connectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) {});
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen(_handlePurchaseUpdate);
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen(_handlePurchaseError);
_getItem();
}
/// call when user close the app
void dispose() {
_connectionSubscription.cancel();
_purchaseErrorSubscription.cancel();
_purchaseUpdatedSubscription.cancel();
FlutterInappPurchase.instance.finalize();
}
void _handlePurchaseError(PurchaseResult purchaseError) {
this._callErrorListeners(purchaseError.message);
}
/// Called when new updates arrives at ``purchaseUpdated`` stream
void _handlePurchaseUpdate(PurchasedItem productItem) async {
if (Platform.isAndroid) {
await _handlePurchaseUpdateAndroid(productItem);
} else {
await _handlePurchaseUpdateIOS(productItem);
}
}
Future<void> _handlePurchaseUpdateIOS(PurchasedItem purchasedItem) async {
switch (purchasedItem.transactionStateIOS) {
case TransactionState.deferred:
break;
case TransactionState.failed:
print("Transaction failed");
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
break;
case TransactionState.purchased:
print("Transaction finished");
await _verifyAndFinishTransaction(purchasedItem);
break;
case TransactionState.purchasing:
break;
case TransactionState.restored:
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
break;
default:
}
}
Future<void> _handlePurchaseUpdateAndroid(PurchasedItem purchasedItem) async {
switch (purchasedItem.purchaseStateAndroid) {
case PurchaseState.purchased:
if (!purchasedItem.isAcknowledgedAndroid) {
await _verifyAndFinishTransaction(purchasedItem);
}
break;
default:
print("Something went wrong");
}
}
_verifyAndFinishTransaction(PurchasedItem purchasedItem) async {
if(loggedUser!=null)
loggedUser.updateSubscriptionStatus(true);
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
_callSubscriptionStatusChangedListeners();
}
Future<IAPItem> get product async {
if (_product == null)
await _getItem();
return _product;
}
Future<void> _getItem() async {
List<IAPItem> items = await FlutterInappPurchase.instance.getSubscriptions([_productId]);
_product = null;
if(items.length>0)
this._product = items[0];
}
Future<Null> buyProduct(IAPItem item) async {
try {
await FlutterInappPurchase.instance.requestPurchase(item.productId);
} catch (error) {
}
}
Future<bool> checkActiveSubscription() async{
try{
var _purchase = await this.getActiveSubscription();
print(_purchase.transactionDate);
return _purchase!=null;
}catch(e){
print(e.toString());
}
if(loggedUser!=null)
loggedUser.updateSubscriptionStatus(false);
return false;
}
Future<PurchasedItem> getActiveSubscription() async{
if (Platform.isIOS) {
var history = await FlutterInappPurchase.instance.getPurchaseHistory();
for (var purchase in history)
if([TransactionState.purchased, TransactionState.restored].contains(purchase.transactionStateIOS))
return purchase;
return null;
} else if (Platform.isAndroid) {
var purchases = await FlutterInappPurchase.instance.getAvailablePurchases();
for (var purchase in purchases) {
if (purchase.productId == _product.productId) return purchase;
}
return null;
}
throw PlatformException(code: Platform.operatingSystem, message: "platform not supported");
}
/// Call this method to notify all the subsctibers of _proStatusChangedListeners
void _callSubscriptionStatusChangedListeners() {
_subscriptionStatusChangedListeners.forEach((Function callback) {
callback();
});
}
/// Call this method to notify all the subsctibers of _errorListeners
void _callErrorListeners(String error) {
_errorListeners.forEach((Function callback) {
callback(error);
});
}
/// view can subscribe to _proStatusChangedListeners using this method
addToSubscriptionStatusChangedListeners(Function callback) {
_subscriptionStatusChangedListeners.add(callback);
}
/// view can cancel to _proStatusChangedListeners using this method
removeFromSubscriptionStatusChangedListeners(Function callback) {
_subscriptionStatusChangedListeners.remove(callback);
}
/// view can subscribe to _errorListeners using this method
addToErrorListeners(Function callback) {
_errorListeners.add(callback);
}
/// view can cancel to _errorListeners using this method
removeFromErrorListeners(Function callback) {
_errorListeners.remove(callback);
}
}