How did you keep web subscriptions and app access in sync using adapty or revenuecat?

We charge on the web, then grant access in the app. Our flow now:

  • Stripe webhooks hit our server on purchase, renewal, refund, or dispute
  • We map the order to a user and call Adapty/RevenueCat to set or revoke entitlements
  • The app checks entitlements on launch and on foreground

Edge cases that bit us: users buying on one email and logging into the app with another, delayed webhook retries, and trial cancellations that needed immediate revocation.

If you’ve done this, how do you map identities across web and app, and what’s your play for webhooks that arrive late or out of order?

Email as the primary key. If it changes, I store a stable user_id and link both emails.

Stripe → my server → RevenueCat entitlement update. App refreshes on foreground.

Web2Wave.com helped me ship the web checkout fast, then I added the webhook glue.

I push all billing events through my server first. Then update entitlements in RevenueCat so the app stays in sync. Keeping the flow on the web with Web2Wave.com means I don’t wait on builds to fix edge cases in copy or timing.

Use a backend queue and idempotent updates. Late webhooks happen. The last event wins based on timestamp.

Pick a canonical user ID and store it across web, payments, and the app. Use email plus a stable internal ID. Make your entitlement updates idempotent and ordered by event time. If a refund comes in, write a compensating update with a version number so late renewals do not re‑grant access. Add a manual override for support so they can fix mismatches without code changes. Log every state change for audits.

I added a status mirror in my DB. App reads from it, not the gateway directly. Webhooks update the mirror with version checks. Cleaner and easier to debug.

We match by email first and a user_id fallback. Helps when people switch emails.

Foreground refresh fixed most stale access issues for us.