Skip to main content

I am testing Stripe integration with RevenueCat and have everything configured and working per your documentation. However, I am running into an issue with Stripe checkout and initial billing failures. When using Stripe checkout, if the initial payment fails the Subscription is automatically placed in an ‘Incomplete’ state. In this scenario, Stripe still emits a customer.subscription.created event which, per your documentation, I handle and post the token to RevenueCat. This subscription immediately fails and Stripe sends an invoice.updated event which I have configured to be sent to RevenueCat. I am then listening to Webhooks emitted by RevenueCat on my own server to do some minimal state management locally.

This series of actions causes 3 events - Initial Purchase, Billing Issue, and Cancellation - to be sent to me by RevenueCat. All 3 events have identical event_timestamp_ms and purchased_at_ms timestamps. In other words, all 3 events appear to be emitted simultaneously.

The problem is, since webhooks are simple web connections, they sometimes are received on my end out of order. When this occurs, RevenueCat appears to have the order of events correct (Started Subscription, Billing Issue, Cancelled due to billing issue), Stripe shows the corresponding subscription in an Incomplete state, but my system, because the events were received out-of-order (tragically, with the INITIAL_PURCHASE event arriving last) shows the user’s subscription as Active.

So my question is if you have any recommendations for how to deal with this scenario?

  • I don’t want to listen for payment failure events from Stripe because I want RevenueCat to be my single source of truth.
  • How is RevenueCat properly ordering these events, because they always appear in the correct order in the Dashboard? Is there another field I can look to, e.g. a sequence number, so I could decide whether I’m receiving out-of-date events?
  • I could pull the subscriber/customer info from RevenueCat for certain events, but a) this bugs me as unnecessarily chatty and b) the response is a little ambiguous - the latest subscription shows an expires_date a month in the future (for a monthly subscription) and the only indication something may be off is the presence of a billing_issue_detected_at value for this subscription

Any guidance or direction would be greatly appreciated. I should also note that this is all being done server-side in NodeJS, so I can’t rely on any of the SDKs and am working directly with the REST APIs.

One other item to note… in the scenario described above the BILLING_ISSUE event has a grace_period_expiration_at_ms value of null. The CANCELLATION event does not contain a grace_period_expiration_at_ms property at all. But I never receive an EXPIRATION notice. I presume this is because Stripe is still holding this subscription in an Incomplete state for 24hrs, at which point I guess I’d eventually get an EXPIRATION? (will need to wait and see on that).

However, I’d rather not have our back-end thinking the subscription was active for that duration.


+1 on this. Does anyone have a recommended practice for implementing webhooks?


Reply