Question

Handling subscribed users who complain they do not have access to their benefits.


Userlevel 2
Badge +7

We get a fair number customer support messages that state something along the lines of “The app forgot that I have premium access and I had to click restore purchases.” We store subscription status to a persistent state in redux so it is confusing to us where they may be losing access to their subscriber benefits. Have other apps dealt with this? Are there common race conditions on startup that can lead to this. 

Anecdotally it seems to happen a lot with family sharing. Sometimes it’s user error (user signs in once, and then accidentally creates a new anonymous account). Looking for common patterns we may be handling incorrectly.

Is it common for apps to get customer support requests like these?


2 replies

Userlevel 2
Badge +4

Hi @jake lynch! This tends to happen most often in the situation you described, where users inadvertently create new anonymous accounts. Other than that, however, I’d be happy to dig in to how you’re checking for subscription status. Are you fetching customerInfo at various times in your app’s lifecycle? How does your redux store work? If you can share some code, that might help us get to the bottom of this.

Thanks!

Userlevel 2
Badge +7

We have a slice, which we call PaywallSlice, that we persist using redux-persist. In it there is a property `isSubscribed` which is a boolean that describes whether or not a user is subscribed. 

 

We then have an async thunk that we use to check if the user is subscribed in RC:
 

/**
* Fetch if the current user is subscribed in RevenueCat
*/
export const fetchRevenueCatSubscriptionThunk = createAsyncThunk<
{
isUserSubscribed: boolean;
latestExpirationDate: string | null;
},
{
uid: string;
revenueCatOffering: string;
}
>(
`${Slices.Paywall}/fetchRevenueCatSubscriptionThunk`,
async ({ uid, revenueCatOffering }, { dispatch }) => {
try {
// Login the current users to RevenueCat
const customerInfo = await Purchases.getCustomerInfo();
const revenueCatIsUserSubscribed =
typeof customerInfo.entitlements.active[revenueCatOffering] !==
'undefined';

const latestExpirationDate = customerInfo.latestExpirationDate;
/**
* Override subscription if `DEV_IS_SUBSCRIBED` is "true"
*/
const isUserSubscribed =
revenueCatIsUserSubscribed
return {
isUserSubscribed,
latestExpirationDate,
};
} catch (err) {
logError(err);
throw err;
}
},
);

we then have a context which we dispatch the thunk

  const setupPurchases = useCallback(
async (uid: string) => {
const { isSubscribed } = store.getState()[Slices.Paywall];

const isConfigured = await Purchases.isConfigured();

/**
* If the Purchases SDK is not configured, then call configure and
* then grab is the user is subscribed
*/
if (!isConfigured) {
/**
* Per docs, this is recommended to be called when the app has access
* to the uid
*
* @see: https://www.revenuecat.com/docs/restoring-purchases#google-play-on-android
*/
await initRevenueCat(uid);
} else {
await Purchases.logIn(uid);
}
// iOS only Superwall setup
if (IS_IOS) {
if (!superwallService.isInitialized) {
await superwallService.initialize(isSubscribed);
}
superwallService.identify(uid);
} else {
// Let Superwall service know it wont be used
superwallService.disable();
}
setHasInitializedPurchases(true);

dispatch(
fetchRevenueCatSubscriptionThunk({
uid,
revenueCatOffering: REVENUECAT_ENTITLEMENT_ID,
}),
);

// Do some other stuff
},
[user?.metadata.creationTime, user?.email, dispatch, store],
);

useEffect(() => {
if (user?.uid) {
setupPurchases(user.uid);
}
}, [setupPurchases, user?.uid]);

 

Reply