Hello everyone :)
The behavior I describe below happens in open testing.
Context:
I’m using RevenueCat for subscriptions in my Flutter app.
Users must have an account linked to my backend in order to subscribe.
That’s why I’m using the option “Keep with original App User ID” in RevenueCat.
Since the stores require a “Restore Purchases” button, I added one, and on click I run:
final user = await Purchases.restorePurchases();
Problem:
Each time I click on my restore purchases button, I get this error:
PlatformException(7, There is already another active subscriber using the same receipt., {message: There is already another active subscriber using the same receipt., readableErrorCode: RECEIPT_ALREADY_IN_USE, code: 7, underlyingErrorMessage: There is already another active subscriber using the same receipt., readable_error_code: RECEIPT_ALREADY_IN_USE}, null)
Which has no sens because, if the user tries to purchase a subscription, the payment modal does show up, and they can successfully subscribe to the app.
What I would like:
-
Ideally, I want to get PlatformException error code 7 only if the user logged into my app actually already has a subscription on their current Apple/Google account, so that I can explicitly tell them that another app account already owns a subscription and they should contact us if they lost access to that account.
-
Otherwise, I would like to simply display a message saying that there is no existing Apple/Google account with an active subscription.
Question:
Do you know why I’m seeing this inconsistent behavior—where restorePurchases tells me the Apple/Google account already has a subscription (even though it doesn’t), while making a new subscription purchase works fine?
I’ve attached the code I use both for restore purchases and for purchasing a subscription.
Thank you for your help :)
// Restore purchase code
_isLoadingRestore.value = true;
try {
final user = await Purchases.restorePurchases();
if (user.activeSubscriptions.isEmpty && context.mounted) {
showSnackBar(context, s.noActivePurchasesFound, durationInSeconds: 5);
}
} on PlatformException catch (e) {
if (e.code == '7') {
if (context.mounted) {
showSnackBar(context, s.subscriptionAlreadyUsedMessage);
return;
}
} else {
if (context.mounted) {
showSnackBar(context, s.errorGeneric);
}
}
} catch (e) {
if (context.mounted) {
showSnackBar(context, s.errorGeneric);
}
} finally {
_isLoadingRestore.value = false;
});
}// Subscribe code
_isLoading.value = true;
final package = widget.offers.firstWhereOrNull(
(element) => element.identifier == _selectedOfferId,
);
if (package == null) {
_isLoading.value = false;
return;
}
await Purchases.purchaseStoreProduct(
package.storeProduct,
);
_isLoading.value = false;
if (mounted) {
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return const RefreshUserLoadingWidget();
},
);
}
await ref.read(userProvider.notifier).fetchUser();
} catch (e) {
_isLoading.value = false;
logger.e(e);
}
