Question

GetCustomerInfo is returning old data, unless called twice?

  • 15 September 2022
  • 15 replies
  • 720 views

Badge +4

Hi,

While testing my subscriptions on IOS with a sandbox account i found a strange behaviour of getCustomerInfo(), needing to call it twice in order to get the correct data.

The way i’ve set my app is to call getCustomerInfo on appLaunch and on appResume, to check for updates. The scenario is the following:

I purchase a subscription and get back info that the entitlement is active and my expirationDate is 5min from now (because of the sandbox account). Then i minimize the app and wait 6 mins, after which i can see the sub renewal transaction appearing in rc dashboard and in the customer details screen. I open the app, which triggers getCustomerInfo, but it’s returning the same data i got from when i first purchased the sub. Then when i minimize the app and open it again, triggering getCustomerInfo for the 2nd time, it return the correct updated data with the new expirationDate.

Is this the expected behaviour? Is this because of Sandbox? Or am i doing something wrong?

P.S. it’s a flutter app, if it makes any difference.


15 replies

Userlevel 3
Badge +8

Where do you init the SDK?  My recommendation is to init the SDK a bit late, not on appLaunch (btw, I am not familiar with Flutter).  In my app, I init the SDK when I create the tab controller (viewDidLoad).  I used to init the SDK on didFinishLaunchingWithOptions and that caused all kind of random issues, thanks to Apple recent iOS startup change in iOS 15.  

Please try and let us know.

Thanks,

Badge +4

Hi @imougy ,

I tried to initialize it late in the app after everything else is loaded and the initial screen is rendered, but i still get the same issue.. I am not sure if this has to do something with the flutter SDK or the caching thats done for getCustomerInfo() or because of sandbox, but it’s really making it unreliable. Easiest way to reproduce is:

  1. Purchase a subscription
  2. Minimize app
  3. Cancel the purchased subscription from the app store manage subs page
  4. Wait for the subscription to expire (in my case for ios sandbox monthly sub is 5 mins)
  5. Open the app,
  6. Call getCustomerInfo() - nothing is changed user’s subscription is still active
  7. Minimize the app
  8. Open it again
  9. Call getCustomerInfo() again - this time correct information is retrieved and user sub is not active

 

Badge +1

Was there ever a true solution to this, the exact same thing is happening for me (build is in apples sandbox environment for storekit). Calling ‘get customer info’ ‘later’ isn’t really going to cut it. Surely the update of stale cache data needs to be within a closure or something similar so we can update our users subscription status following the asynchronous call by revenue cat on initial app load.

I’ve outlined a timeline of how I see it from a mixture of revenue cat debug logs and my own print statements

  1. Revenue cat config and debug settings are applied via appDelegate and initial Revenue cat pull is initiated as stated in debug messages:

2022-10-28 15:45:35.134382+0200 Vocable[26790:2040491] [Purchases] - DEBUG: 👤 Identifying App User ID

<NON RELEVANT DEBUG LOGS REMOVED FOR BREVITY>


2022-10-28 15:45:35.240780+0200 Vocable[26790:2040491] [Purchases] - DEBUG: ℹ️ Vending CustomerInfo from cache.

  1. Below two lines are printing from my landing VC model from which the users subscription status is set using method :- Purchases.shared.getCustomerInfo { (customerInfo, error) in

YEARLY SUB EXPIRATION 28 Oct 2022 at 14:41:07
MONTHLY SUB EXPIRATION 28 Oct 2022 at 15:25:21

**Following these print statements (print statements formatted by myself and not RC’s debug logs) my users subscription status is set and the user advances.

  1. Revenue cat completes update of stale data and updates cache:

<NON RELEVANT DEBUG LOGS REMOVED FOR BREVITY>

2022-10-28 15:45:42.576591+0200 Vocable[26790:2040724] [Purchases] - DEBUG: ℹ️ API request completed: GET /v1/subscribers/$RCAnonymousID:edc791d7aa324f34a3fe54122c96d71f 304
2022-10-28 15:45:42.623087+0200 Vocable[26790:2040724] [Purchases] - DEBUG: ℹ️ Sending latest CustomerInfo to delegate.
2022-10-28 15:45:42.623206+0200 Vocable[26790:2040724] [Purchases] - DEBUG: 😻 CustomerInfo updated from network.
2022-10-28 15:45:42.625030+0200 Vocable[26790:2040724] [Purchases] - DEBUG: 😻 CustomerInfo updated from network.

 

Unfortunately by the time the local cache has been updated my user is logged in and has access to premium features even though their subscription has expired (upon close and open of app or another get status the correct subscription status is offered from the cache).

It would be far better if we could utilize a closure on the initial update of stale cached data before allowing the user to advance.

Hopefully there is a simple solution to this that I’m missing.

Userlevel 3
Badge +8

This seems an issue and solution is not delaying the init unfortunately. 
the good news is of course things settle down after a while. I understand you want instant reflection of the purchase status but it seems a lower status bug in the SDK.

Badge +1

Is there a ways to initiate an observer for successful completion of data update, I could at least update the UI if needed?

Userlevel 3
Badge +8

Since we are talking about workarounds, have you tried calling the getCustomerInfo twice, put a delay so the second call is executed after 1 second or so?

I don’t like workarounds like this but if it works, well!

Userlevel 2
Badge +3

Is there a ways to initiate an observer for successful completion of data update, I could at least update the UI if needed?

 

So I was hitting this exact scenario. When the app was launched after the sub had expired I saw RC realize it and start a network request:

[Purchases] - DEBUG: ℹ️ applicationDidBecomeActive

BEFORE the network request is complete my code in the onAppear calls Purchases.shared.getCustomerInfo and gets back cached data.

[Purchases] - DEBUG: ℹ️ Vending CustomerInfo from cache.

Then the network request completes with the updated data (that nothing was using yet). When I minimized my app again and launched it for the second time the onAppear saw this new data instead of the old cached data.

The way I got around this was by adding more logic to the didSet for the customerInfo in UserViewModel. I was looking specifically for the expiration date so I had my logic do its magic with the newest value when customerInfo changes.

Badge +2

I am having the same issue in flutter: even after waiting more than 5 minutes after the subscription has expired (manually cancelling the test subscription in Google Play, then wait e.g. 10 minutes), the first call to getCustomerInfo still returns that the subscrition is active. Only the second attempt makes it work… What are we missing here?

Thanks!

Badge +5

@Bence Blaske I am having the exact same issue in Flutter. No matter how many times I delete and reinstall the app, whenever I install my app from Google Play Store, First run is active no matter what I do. I couldn’t check if this issue is also happening for other users or not. But basically, 5 min cache is kinda dangerous for my app, reaching the premium once in my app is worst thing ever because then they would probably delete the app.

@Ahmet Aziz Besli @Bence Blaske I was the same issue for a while and even the workarounds didn’t help, after searching in the flutter plugin I found this

Purchases.invalidateCustomerInfoCache();

it basically makes the customer info cache invalid so on the next getCustomerInfo call it is certain that the latest object is retrieved from RC backend, I think this is the optimal solution

Badge +5

Running into the same issue with react native and invalidateCustomerInfoCache() just does not resolve. And relaunching the app just makes everything stop working (cant even call getCustomerInfo() anymore).

Has anyone found a better solution? Or is there a way NOT to use the cache? I am ok to have getCustomerInfo() call the servers each time since I have my own “cache” based on back-end + webhooks.

Badge +3

@vic-a563d7 I encountered the same issue on Android and this solved it for me:

Purchases.sharedInstance.getCustomerInfoWith(    onSuccess = {            },    onError = {            },    fetchPolicy = CacheFetchPolicy.FETCH_CURRENT)

 

The default behavior is CACHED_OR_FETCHED - returns the cached data if available (even if stale). If not available, fetches up-to-date data. If cached data is stale, it initiates a fetch in the background.

 

(Thanks to my colleague Laurentiu for the tip)

Badge +5

Thanks @Radu S , which SDK is this? I am using the react native one and I don’t see a way to force the fetch to be “current”.. I assume this means get it from network always and avoid the cache?

Badge +3

Thanks @Radu S , which SDK is this? I am using the react native one and I don’t see a way to force the fetch to be “current”.. 

Android SDK. Sorry, I assumed the option is available for react native too.

 

I assume this means get it from network always and avoid the cache?

Yes

Badge +5

Got it. I don’t think this is available in the react native SDK.. Not sure who can confirm..

Reply