Please not that the same is happened when I get a SUBSCRIPTION_PAUSED event. In this case I would except the entitlements to be removed, but customerInfo() still lists active ones..
FWIW, I looked at the docs here and here, but not able to see why it is still cached and outdated data behind customerInfo(). I have waited more than 1 hour without and no changes (data still shows active entitlements). I’ve called customerInfo() multiple times after. I’ve also tried calling invalidateCustomerInfoCache() and this just hangs and does not resolve the promise in react native. One thing to note is that the firebase back-end is correct, it shows the right claims removed now as the side-effect of the EXPIRATION event I assume.
Hi,
Happy to help here. The customerInfo object should be updated as soon as we send that EXPIRATION webhook. Can you print out the object and paste it here? Is the .active property set to true, or are there just entitlements in the array (but with an expiration date in the past)?
yes sure. but I am in react native so I dont see an active boolean. just objects.
here is the output (names redacted). basically I make the purchase with the test account → go into OS and cancel it → then when I see expiration came from the webhooks → go into device and call customerInfo() again and still see active subscriptions for a few minutes. Eventually it does catchup..
{
"activeSubscriptions": p
"v1_premium_6mo"
],
"allExpirationDates": {
"v1_premium_1mo": "2023-11-09T04:20:39Z",
"v1_premium_6mo": "20 23-11-10T00:35:12Z"
},
"allExpirationDatesMillis": {
"v1_premium_1mo": 1699503639000,
"v1_premium_6mo": 1699576512000
},
"allPurchaseDates": {
"v1_premium_1mo": "2023-11-09T04:17:39Z",
"v1_premium_6mo": "2023-11-10T00:17:12Z"
},
"allPurchaseDatesMillis": {
"v1_premium_1mo": 1699503459000,
"v1_premium_6mo": 1699575432000
},
"allPurchasedProductIdentifiers": i
"v1_premium_6mo",
"v1_premium_1mo"
],
"entitlements": {
"active": {
"ent_premium": r
Object
]
},
"all": {
"ent_premium": r
Object
]
}
},
"firstSeen": "2023-10-25T05:29:51Z",
"firstSeenMillis": 1698211791000,
"latestExpirationDate": "2023-11-10T00:35:12Z",
"latestExpirationDateMillis": 1699576512000,
"managementURL": "https://apps.apple.com/account/subscriptions",
"nonSubscriptionTransactions": c
],
"originalAppUserId": "_my_custom_id_",
"originalApplicationVersion": "1.0",
"originalPurchaseDate": " 2013-08-01T07:00:00Z",
"originalPurchaseDateMillis": 1375340400000,
"requestDate ": "2023-11-10T00:26:34Z",
"requestDateMillis": 1699575994000
}
here is the full object coming out of customerInfo()
the pattern I noticed is that always second call to customerInfo() is correct but first one after expiration is not even if waiting longer..
{
"allPurchasedProductIdentifiers": [
"v1_premium_6mo",
"v1_premium_1mo"
],
"allExpirationDates": {
"v1_premium_1mo": "2023-11-10T03:49:09Z",
"v1_premium_6mo": "2023-11-10T00:35:12Z"
},
"originalApplicationVersion": "1.0",
"originalAppUserId": "_CUSTOM_",
"firstSeen": "2023-10-25T05:29:51Z",
"managementURL": "https://apps.apple.com/account/subscriptions",
"requestDateMillis": 1699587926000,
"allPurchaseDatesMillis": {
"v1_premium_6mo": 1699575432000,
"v1_premium_1mo": 1699587969000
},
"allExpirationDatesMillis": {
"v1_premium_6mo": 1699576512000,
"v1_premium_1mo": 1699588149000
},
"latestExpirationDateMillis": 1699588149000,
"allPurchaseDates": {
"v1_premium_1mo": "2023-11-10T03:46:09Z",
"v1_premium_6mo": "2023-11-10T00:17:12Z"
},
"requestDate": "2023-11-10T03:45:26Z",
"entitlements": {
"all": {
"rc_ent_premium": {
"isActive": true,
"productIdentifier": "v1_premium_1mo",
"isSandbox": true,
"latestPurchaseDate": "2023-11-10T03:46:09Z",
"latestPurchaseDateMillis": 1699587969000,
"expirationDateMillis": 1699588149000,
"billingIssueDetectedAt": null,
"expirationDate": "2023-11-10T03:49:09Z",
"identifier": "rc_ent_premium",
"willRenew": true,
"periodType": "NORMAL",
"originalPurchaseDateMillis": 1699239095000,
"productPlanIdentifier": null,
"unsubscribeDetectedAt": null,
"unsubscribeDetectedAtMillis": null,
"ownershipType": "PURCHASED",
"billingIssueDetectedAtMillis": null,
"store": "APP_STORE",
"originalPurchaseDate": "2023-11-06T02:51:35Z"
}
},
"active": {
"rc_ent_premium": {
"originalPurchaseDate": "2023-11-06T02:51:35Z",
"originalPurchaseDateMillis": 1699239095000,
"billingIssueDetectedAtMillis": null,
"latestPurchaseDate": "2023-11-10T03:46:09Z",
"latestPurchaseDateMillis": 1699587969000,
"expirationDateMillis": 1699588149000,
"billingIssueDetectedAt": null,
"expirationDate": "2023-11-10T03:49:09Z",
"identifier": "rc_ent_premium",
"productIdentifier": "v1_premium_1mo",
"willRenew": true,
"periodType": "NORMAL",
"productPlanIdentifier": null,
"unsubscribeDetectedAt": null,
"unsubscribeDetectedAtMillis": null,
"ownershipType": "PURCHASED",
"isSandbox": true,
"store": "APP_STORE",
"isActive": true
}
}
},
"originalPurchaseDateMillis": 1375340400000,
"activeSubscriptions": [
"v1_premium_1mo"
],
"latestExpirationDate": "2023-11-10T03:49:09Z",
"firstSeenMillis": 1698211791000,
"nonSubscriptionTransactions": [
],
"originalPurchaseDate": "2013-08-01T07:00:00Z"
}
Ah, this sounds like it’s caused by the customerInfo cache.
We do have a method to invalidate the cache if you want to use that
Thank you for your reply but this does not help @Ryan Glanz since in my comment above I’ve talked about both points and the issues I am still having.
Again, I looked at invalidate cache and it does not work plus documentation says not to use. Is there a way to force reload the customerInfo? The method does not accept any parameters (react native sdk). I have things on the back-end that need to happen when a webhook expiration fevent happens. as well as the user in the client needs to see things correctly. if these 2 are too far apart it can cause confusion for both user and developer. The documentation says the cache if max of 5min but that was not the case in my testing. The webhook event came in and it took a long time for customerInfo() to reflect not more active entitlements. One time I got tired and even had to reinstall the app.
I am still unable to get invalidateCustomerInfoCache() to resolve. I was planning to compare the back-end to see if the EXPIRATION webhook was called and if the client is still showing active entitlement to call invalidateCustomerInfoCache() and then customerInfo() right after to pull latest data. But I just can’t get past clearing the cache.
Any ideas @Ryan Glanz ?
Hi,
Ah sorry, I missed the invalidateCache info in your prior comment.
I’m wondering if the EXPIRATION hasn’t made its way through the Sandbox environment → RevenueCat yet (i.e., it is not reflected in our data), or if it is and this is purely a customerInfo cache thing.
To check, if you make and cancel a purchase, then use our GET /subscribers api endpoint (you could just use curl on a terminal or postman or something), is that customerInfo object that gets returned correct? Then if you call getCustomerInfo in the SDK, is that still outdated?
That will let us know if the underlying data is correct. It sounds like it should be based on the Firebase state.
Next, do you have our customerInfo listener implemented? That should keep the customerInfo object up to date with purchases/cancellations without needing to manually invalidate.
You could also set up server notifications, which allow RevenueCat to get information from Apple even faster.
Thanks @Ryan Glanz.
The API returns the same objects as the customers document collection from the firebase extension. So it matches what the back-end already has. To know if it is correct/accurate that is a different question. This objects does not have like an “active” entitlement node like the client SDK. It just has expiration dates in UTC so those are the same before and after purchase expired.
FWIW, I am already using server notifications and I’ve confirmed they are timely.
I will look into customerInfo listener, but that is just an event callback for when the data changes right? Not really tied to how and when the cache gets updated. I am not really looking to react to changes in customerInfo, I just want customerInfo to return most up to date info when called since it is being called on screens open etc.
I was able to confirm it is a client cache issue.
After EXPIRATION the client still shows active entitlements. But the back-end has a null management_url now as well as the revenue cat API (so that tells me the backend is correct). The expiration dates are in the past too. So only the client still shows active with past expiration dates. The managementURL on client is also populated.
I just can’t get this cache to clear and it’s totally making this out-of-sync issue bad. Maybe this is only in debug mode with timers or some other issue but I am not sure if I should just trust it and go to production..