Skip to main content

Hi,

 

I have a mobile app (iOS/Android) and the backend is built with Django+MySQL. My app offers a monthly/yearly “premium user” subscription and we’re using RevenueCat to handle IAPs. But we also want to keep the subscription status (isPremiumUser or not) of a user in our own database’s user records as well.

 

I read through the documentation for RevenueCat webhooks and came across this excerpt.

Webhooks are commonly used to keep a subscribers status in sync with RevenueCat across multiple systems. To simplify the logic of handling different webhook types, we recommend creating a polling system using the GET /subscribers REST API to sync the subscription status of the customer from RevenueCat to your database. Then, each webhook event can simply be a trigger to call this sync function. This approach has a few benefits and can make your system more robust and scalable.

 

The way I understand is, below are the steps to implement such a system.

1. Register URL to receive webhook events.
  1.1. Send 200 response back to RevenueCat servers when a webhook event is received.

2. Every time a webhook is received, get the 'original_app_user_id' from the response.

3. Call /subscribers/<original_app_user_id> to get the subscriber info.

4. Determine whether the user currently has an active subscription.

5. Update my database's user object 'isPremiumUser' property accordingly.

 

Now my question has a few parts.

  1. Is my approach correct? Am I missing any steps?
  2. How do I actually determine whether the user is subscribed or not? What fields in the JSON should I check?
  3. In the REST API’s docs, it says if I call the /subscribers endpoint with a user id and that user doesn’t exist, it will create one. Wouldn’t that be a problem?

 

I’d really appreciate any help to get these clarified. Thanks in advance.

  1. Is my approach correct? Am I missing any steps?

 

That sounds about right to me!

 

  1. How do I actually determine whether the user is subscribed or not? What fields in the JSON should I check?

 

One way is to loop through the entitlements dictionary and check the expires_date of each entitlement object. If the date is in the future, the user still has access to the entitlement. If you’re not using entitlements in RevenueCat, loop through the subscriptions object instead and check the expires_date on each subscription.

 

  1. In the REST API’s docs, it says if I call the /subscribers endpoint with a user id and that user doesn’t exist, it will create one. Wouldn’t that be a problem?

 

You can tell if the user was created by checking the HTTP status code of the response. If it’s 201, that means the user was created as a result of this request. If it’s 200, the user existed before you made this request. So you can check if the status code is 201, and if you didn’t want to create the user, you can delete them.

 

-- Sharif


 

One way is to loop through the entitlements dictionary and check the expires_date of each entitlement object. If the date is in the future, the user still has access to the entitlement. If you’re not using entitlements in RevenueCat, loop through the subscriptions object instead and check the expires_date on each subscription.

 

 @RC Support What are we expected to do if the expires_date is in the future (user has access until end of billing period)? Is there a webhook that triggers again for a user when the expires_date is reached or are we expected to somehow schedule the entitlement to be removed from the user on the expires_date?


 

One way is to loop through the entitlements dictionary and check the expires_date of each entitlement object. If the date is in the future, the user still has access to the entitlement. If you’re not using entitlements in RevenueCat, loop through the subscriptions object instead and check the expires_date on each subscription.

 

 @RC Support What are we expected to do if the expires_date is in the future (user has access until end of billing period)? Is there a webhook that triggers again for a user when the expires_date is reached or are we expected to somehow schedule the entitlement to be removed from the user on the expires_date?

 

For my server side code, in addition to checking the isPremiumUser I use an extra check to make sure that their subscription is still active by checking the expiration date. 

So in your case I think before returning true for isPremiumUser you could perform another check in your database to make sure that the expires_date is in the future.

 

 


Hi, I have a similar use case and found this post quite helpful. I have a follow on question regarding this:

I read through the documentation for RevenueCat webhooks and came across this excerpt.

Webhooks are commonly used to keep a subscribers status in sync with RevenueCat across multiple systems. To simplify the logic of handling different webhook types, we recommend creating a polling system using the GET /subscribers REST API to sync the subscription status of the customer from RevenueCat to your database. Then, each webhook event can simply be a trigger to call this sync function. This approach has a few benefits and can make your system more robust and scalable.

 

If I am receiving Webhook requests on my backend, why do I need to make the GET /subscribers request at all? Can I not derive what I need to know to update my database right from the event payload? Maybe the ordering of the events coming in is not guaranteed or something like that?

 

Thanks!


@RC Support 

Im stuck implementing webhooks and will probably call the REST interface like suggested in the documentation to keep your database and user subscription state in sync.

I think there should be a way more simplified web hook.

  • Listen for web hook events
  • On each change that is affecting user you get the current state of the user as an event and save or update your database entry with that complete event

This way we don't have to implement logic for every single state. OPs the question above are missing the case for “restore purchases” for example.

There is no app_user_id, just transfer_from and transfer_to and for what ids you need to call the /subscribe interface? for all or only for the non anonymous ones but for both arrays to prevent to let both users have a entitlement?

A complete example with all the user cases at least for the web hook → REST API call would be nice.


In the webhook events, as I understand we will receive the app userId, so we received that userId.

 

It can’t be non-existing user if the user exists? How it can be unexisting user?


Here is what I've personally done:

Handle events such as "INITIAL_PURCHASE", "RENEWAL" and "BILLING_ISSUES", and check the `expiration_at_ms` or `grace_period_expiration_at_ms` (for BILLING_ISSUES, cause it will automatically give you the grace period defined on the respective stores).

Basically with those 3 events, I think I can be sure to have the correct expiration date of a subscription in my database, without having to call the /subscriber API.

I do not like the fact to have to do an extra call to the API where I am supposed to have all the required information in the web-hook to update my user status. While calling the /subscriber API is a safer approach (because you are always using RC as a source of truth in this case), I also do not believe it to be scalable as RevenueCat has a quota on the API requests / seconds, and you may get a potential "429 - Too Many Requests" if you have a very successful app with a lots of subscribers.

Correct me if I am wrong though, I'm willing to learn more on what are the best practices to handle user subs sync in our own database.


Hi.  I’m still struggling with exactly what was brought up in this thread.  I see that @schankam ‘s last reply hasn’t had any responses and I’m hoping to revive this topic @RC Support .

The revCat docs clearly outline exactly what @Dylan wrote as best practices, yet there is no documentation on exactly how to implement this with v2 of the REST API.  I’ve banged my head against the wall trying to get the API (v2) results to return ( I just get an empty user object), and the v1 API gives me a 404 error.

Reading the above comments sure suggests this latter step is not necessary…

Why not return the customer information object with each webhook response?  Why a second step - an external api request to revCat?  Why doesn’t the webhook event just add the customer info object to the results we already receive in our webhook?   Can @RC Support please provide insight into why this isn’t the case?


Turns out the the reason I wasn’t able to get any subscription info is that you have to signup for the v2 Beta program, which wasn’t obvious in the revCat docs at the time of this writing (hopefully this will be updated by now!)


+1 for a very simple webhook (durable with retry) that returns the userid and a list of their currently active entitlements and their expiration date. This is one of those things that currently feels 100x harder than expected


Reply