Question

Getting active Entitlements from an expired subscription iOS

  • 11 February 2022
  • 16 replies
  • 813 views

Badge +5

Hello, 

Any ideas as to why the app will be receiving active entitlements even months after the subscription has expired? 

Currently I have an app in production, and while going through some of the “Expired” accounts, I noticed that a few of them are still active with the application. I have checked, and double checked, tested the logic of the app and it seems as if it should block the person from using the app and being directed to a subscription page. 

However, the only reasonable explanation as to why the users are still having access to the app would be if RevenueCat’s SDK is sending back an active entitlement. 

 

I have a method, that each time the app becomes active is called, this method is in charge of verifying that the user has an active subscription each time the app becomes active. 

Within “checkForActiveSubscription()” I call the “purchaserInfo” to get the latest information from the purchaser as per the instructions on how to check for active subscription on the documents. 

Side note: On the documents it says to call “getPurchaserInfo()” and I think this method was renamed, but it is still showing on the documents for checking active subscriptions. 

 

Once the “purchaserInfo” is called, if no active entitlement is found, then access is denied. Otherwise if one entitlement is active, then we inerated through the list and verify which entitlement the user has subscribed to and give them access to that level. 

With this said, this logic should work, unless I am overlooking at something here. 

If any Revenue Cat staff can shine a light would be great.
 

    /// Checks if the user has an active subscription.
fileprivate func checkForActiveSubscription() {
guard !isPurchaseInProgress else { return }

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yy"

Purchases.shared.purchaserInfo { (purchaseInfo, error) in
guard let entitlements = purchaseInfo?.entitlements.active else { return }
guard !entitlements.isEmpty else { self.denyAccess(to: user) ; return } /// Denies access to the user if no entitlement is active
guard let highestEntitlement = self.getHighestRankedSubscription(entitlements: entitlements) else { return }

for (key, entitlement) in highestEntitlement where entitlement.isActive {
switch key {
case "Economy":

// Gives access to the user on this level

case "Business":

// Gives access to the user on this level

case "First Class":

// Gives access to the user on this level
default: break
}
}
}
}

 


16 replies

Badge +5

Hello @sundeep 

Would you assist me with this problem?

Userlevel 3
Badge +8

Hi @Jhoan Arango,

What SDK version are you using? Do you know if this is happening consistently for those users or only occasionally on first launch? Also, do you happen to have debug logs that reproduce this?

Badge +5

Hello @sundeep

No debug logs, this seems to be happening on the app that is in production. I have some users and their user ID’s if it helps you to investigate the issue further. What I see on the on the “Customer Profile” in RevenueCat is that their subscription has expired a few months ago. However it shows last login recently, and looking at their account on my back-end it also shows information that they would not have if they didn’t have access to the app. 

The users are on the latest version of the app, that has “Purchases 3.12.2”. I am assuming is consistent because each time the app becomes active, I use the purchaserInfo() method to verify its active status.

The code I used on my original post, is the one being called each time the app is active. In that method, if RevenueCat was to return 0 active Entitlements, then the app would display a screen that does not allow the user to bypass unless they subscribe again. The user’s expired information is saved locally to make sure that screen is displayed when the person tries to open the app again. If that was to fail, then the app will open momentarly, and it will verify their information once again by calling purchaserInfo(). 

Here is a screenshot of one of the user’s, the information on the screenshot is the one that comes from their last activity on the “Customer Profile”. There you will see that their subscription has expired a few months ago, and it also shows the active entitlements. Not sure if those entitlements should be there by default, or if they are shown for when that activity happened at that given moment. But I would assume that if their subscription has expired, the “entitlement ids” should be empty. 

Please do let me know, if you need anymore information to help you investigate this. I will on my end try to replicate this and see if we can get some dubs logs.

 



Jhoan 

Userlevel 5
Badge +9

Hey Jhoan,

Just jumping in here with a quick suggestion. Since you know the user that this is happening to, try making a request to the GET /subscribers endpoint and see what active entitlements are returned. The SDK is a wrapper around our API (and purchaserInfo is a wrapper around that GET endpoint) so you should be able to see exactly what the SDK is seeing by running that request yourself (you can even do it in your browser using that documentation page I linked to.)

If the request isn’t returning an active entitlement, then it’s likely an issue in your app (maybe the getHighestRankedSubscription function?) since we haven’t received any reports where the SDK behavior doesn’t match with the API response regarding active entitlements.

Another possibility may be that the cache that the SDK uses to reduce data usage on the device is a little stale. The cache is refreshed if it’s older than 5 minutes when you call purchaserInfo and whenever the app is launched, so there may be a very short time when the user has access after their subscription expired because the SDK is refreshing the cache. This is almost never a problem because refreshing the cache is quick but you can reduce the time a cache is stale by calling purchaserInfo anytime the user navigates to a part of your app that should be gated by an in-app purchase. If the behavior you’re seeing persists longer than a few minutes, then the cache is not the cause.

If the request is returning an active entitlement, then they very likely still have an active subscription somehow and we can look into it.

Badge +5

Hello @sharif , 

Thank you for your reponse, this URL does help because it does give me a clue as to what is happening. As I suspected, the response comes with active entitlements after testing. 

Here is a screenshot, I removed the product identifiers, but i can confirm those are the products I am using. 

 



And here is another screenshot of the “subscription”.

 

 

 

As you can see, the susbcription was purchased back in 2020-20-10, which expired on 2021-20-10. Which at this point, it should already have removed the active entitlements. 

Thank you for your reponse, I really appreciate you helping. 

EDIT: I would also like to mention that is not only one user, this is with several users. This is just one example from the ones I have found. 

Jhoan 

Badge +5

Hello @sharif , 

I am sure you are busy, but I would like to know if you have been able to look into this issue. As you may know this can potentially be giving people free access to the app, or perhaps they may be with an active Subscription but is not reflecting correctly on RevenueCat. 

Please let me know.

Jhoan 

Userlevel 5
Badge +8

Hey @Jhoan Arango

 

Adding some clarification here: 

Although the API returns the entitlements, those won’t be marked active by our SDK, they’ll get filtered out of the active property, but they’ll show in all

This is so that you still have a way of seeing entitlements the user used to have, should you need them. 

If you’re curious about how that logic works on our side, here’s the code that filters out the active entitlements from the rest in 3.12.2.

 

And the code you shared seems correct at least from what we can see. 

I’m curious about this bit: 

The user’s expired information is saved locally to make sure that screen is displayed when the person tries to open the app again. 

 

Could you clarify what happens there? Is there a chance that the local cache might be getting surfaced instead? 

 

Building on top of @cody ‘s idea to check the API directly, perhaps you can check what the SDK is giving you by calling logIn (or identify if you’re using that) with an appUserID that you know is seeing this issue, and checking the active entitlements in their PurchaserInfo

Badge +5

Hey @Andy , 

Thank you for joining us on trying to solve this issue. 

The user’s expired information is saved locally to make sure that screen is displayed when the person tries to open the app again. 

 

This means that when the user’s subscription is expired, the app will save the status locally, so that the next time the user opens the app, it will display a view asking him to subscribe again. That view, is a subscription view, where it will display the options he has for subscribing. At which point when that view is presented, it verifies with RevenueCat by checking their active entitlement. In other words, is just a method to double check and make sure that they do not get access to the app without a subscription. 

The process in the app is very simple, when the app becomes active, I do a request to RevenueCat as shown on my previous post, I receive a response and verify if there are any active entitlements, if there are any, we grant access, if not, we deny access and present a pay wall to re-subscribe or restore purchases. 

That logic is very straightforward withing my code, and I have doubled checked on it a few times. The only way someone can bypass that process is if RevenueCat’s SDK is returning active entitlements.

As shown on those screenshots above, I used the Get subscribers endpoint as suggested by @sharif, where the response is that I am receiving “Entitlements” from that user’s account. Which means to me, that it has nothing to do with the verification code I use on the app. I am using the code suggested on the documentation to verify active entitlements, which should mean an active paid subscription. At this point, I do not know what else to do since it seems is out of my hands.

My question to you is this, if I am calling the API with the link suggested, and I get “entitlements”, does that mean the user has an “active status” or “active entitlements”? 

Here is a new screenshot for reference: ( I removed the product identifiers but I can confirm those are the ones from the app ) 

 



Thank you for your time

Jhoan 

Userlevel 2
Badge +6

My question to you is this, if I am calling the API with the link suggested, and I get “entitlements”, does that mean the user has an “active status” or “active entitlements”? 

 

No, it means the subscribers had entitlements at some point. In order to check if they are active, you would have to compare the `expires_date` to the current date. In the particular case of the screenshot, they are not active entitlements, since they expired in October 2021.

Badge +5

Hello @Miguel Carranza , 

Thank you for your input.

Please help me understand this then, 

  1. How can I check if the user has active entitlements with the SDK?
    According to the documents I can use:
if !purchaserInfo.entitlements.active.isEmpty {
//user has access to some entitlement
}

 

and now you are saying that I must check the entitlements “expires_date” to verify if is active? Or are you saying that this is the behavior from the link I used to verify the user’s information? 

 

 

Userlevel 2
Badge +6

The latter. When using the get subscribers API endpoint, RevenueCat servers will return all the entitlements, so it’s the responsibility of the consumer of the API to check the expiration date to figure out if the entitlement is active or not. The SDK will do the date comparison internally when calling `active`.

Badge +5

Got it, 
 

So who, or how can I verify that the SDK is returning the correct information? The only way for someone to get access to the app is IF they are receiving active entitlements. 

Thank you

Userlevel 5
Badge +8

 

@Jhoan Arango in my previous comment I said 

Building on top of @cody ‘s idea to check the API directly, perhaps you can check what the SDK is giving you by calling logIn (or identify if you’re using that) with an appUserID that you know is seeing this issue, and checking the active entitlements in their PurchaserInfo

 

I think you should give that a shot, since it’ll show you exactly what the SDK is doing and how it’s classifying entitlements as active vs inactive. 

 

Let us know how it goes! 

 

Badge +5

@Andy , 

Thank you for this, I have been able to look at it. This particular user is returning entitlements, but when looking at the “active” dictionary it does return empty. 

I have to do more testing and will investigate further 

Userlevel 5
Badge +8

@Jhoan Arango Thanks for the update!! Let us know if there’s anything else we can do to help you figure this out. 

Badge +1

@Andy , 

Thank you for this, I have been able to look at it. This particular user is returning entitlements, but when looking at the “active” dictionary it does return empty. 

I have to do more testing and will investigate further 

Hi Jhoan Arango, I am getting this issue with entitlements. Were you be able to resolve this “Active” entitlement issue?

Reply