Create a single subscription in the app store connect account and run the following commands on iOS side multiple times to buy and restore subscriptions
InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam); or InAppPurchase.instance.restorePurchases();
For example: buyNonConsumable() and on successful test transaction, delete the app and reinstall the app. Then execute the restorePurchases() to restore your subscription.
Repeat the above step 5 to 7 times and then you will start receiving wrong and huge list of AppStorePurchaseDetails in subscription stream.
Expected results As there is only one non-consumable subscription, there should be only one transaction details object on purchasing or restoring a subscription by using the following code:
InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam); or InAppPurchase.instance.restorePurchases();
This feature works perfectly fine on android side.
Future<void> _onPurchaseUpdate(
List<PurchaseDetails> purchaseDetailsList) async {
for (final purchaseDetails in purchaseDetailsList) {
if (purchaseDetails.status == PurchaseStatus.canceled) {
finishTransaction();
paymentState.value = PaymentState.failed;
errorMessage = "Purchase Cancelled";
update();
Future.delayed(Duration(seconds: 1))
.then((value) => paymentState.value = PaymentState.init);
}
if (purchaseDetails.status == PurchaseStatus.purchased) {
if (Platform.isIOS) {
final String? IOSpurchaseToken;
IOSpurchaseToken =
purchaseDetails.verificationData.localVerificationData;
if (IOSpurchaseToken.isNotEmpty &&
IOSpurchaseToken != null &&
IOSpurchaseToken != "") {
print("IOS >> $IOSpurchaseToken");
final checkPurchaseTokenParams = CheckPurchaseTokenValidityParams(
deviceType: 'IOS',
token: IOSpurchaseToken,
price: _purchasePrice,
);
checkPurchaseTokenValidityUseCase(checkPurchaseTokenParams)
.then((result) => result.fold(
(failure) {
errorMessage = failure.toString();
paymentState.value = PaymentState.failed;
update();
},
(SubscriptionDetails isValid) {
_subscriptionDetailsData = isValid;
SharedPrefUtility.setisPremium();
paymentState.value = PaymentState.success;
finishTransaction();
if (!isNavigated &&
!SharedPrefUtility.getIsVisitedToWelcomeScreen()) {
locator.get<NavigationService>().pushNamed(
AppRoutes.welcomePlusUser,
arguments: <String, dynamic>{"isFirst": false});
SharedPrefUtility.setIsVisitedToWelcomeScreen();
isNavigated = true;
update();
}
update();
},
));
}
}
Map? purchaseData;
purchaseData =
json.decode(purchaseDetails.verificationData.localVerificationData);
final susbcriptionID = purchaseData?["productId"];
final packageName = purchaseData?["packageName"];
final purchaseToken = purchaseData?["purchaseToken"];
if (purchaseData?["acknowledged"] == false) {
final InAppPurchaseAndroidPlatformAddition androidPlatformAddition =
InAppPurchase.instance
.getPlatformAddition<InAppPurchaseAndroidPlatformAddition>();
await androidPlatformAddition.consumePurchase(purchaseDetails);
}
InAppPurchase.instance.completePurchase(purchaseDetails);
final checkPurchaseTokenParams = CheckPurchaseTokenValidityParams(
deviceType: "ANDROID",
token: purchaseToken,
price: _purchasePrice,
packageName: packageName,
subscriptionID: susbcriptionID,
isUpgraded: _isSubscribed);
checkPurchaseTokenValidityUseCase(checkPurchaseTokenParams)
.then((result) => result.fold(
(failure) {
errorMessage = failure.toString();
paymentState.value = PaymentState.failed;
update();
},
(SubscriptionDetails isValid) {
_subscriptionDetailsData = isValid;
SharedPrefUtility.setisPremium();
paymentState.value = PaymentState.success;
if (!isNavigated &&
!SharedPrefUtility.getIsVisitedToWelcomeScreen()) {
locator.get<NavigationService>().pushNamed(
AppRoutes.welcomePlusUser,
arguments: <String, dynamic>{"isFirst": false});
SharedPrefUtility.setIsVisitedToWelcomeScreen();
isNavigated = true;
update();
}
update();
},
));
update();
} else if (purchaseDetails.status == PurchaseStatus.error) {
errorMessage = purchaseDetails.error.toString();
paymentState.value = PaymentState.failed;
update();
} else if (purchaseDetails.status == PurchaseStatus.restored) {
print("Restore Purchased Called");
}
}
purchaseDetailsList.clear();
}