Biggest fear with web-first purchase was a user paying and then seeing a locked screen in the app.
Our current setup:
- Web checkout completes (Stripe). Webhook hits our backend. We upsert user and subscription, then call Adapty/RevenueCat REST to grant entitlement.
- Deep link opens app with a short token. App fetches profile by user_id and skips paywall.
- We built a retry queue for webhooks and use idempotency keys so we don’t create dupes.
- On launch, the app does a quick entitlement refresh so late webhooks still unlock within seconds.
Any gotchas here, especially around trials, refunds, or race conditions when the app opens before the webhook processes?
I push web receipts into RevenueCat immediately on webhook, then confirm in the app with a refresh on first launch.
Use one stable user_id across web and app. Add idempotency keys.
If app opens early, I show a loading state for 1–2 seconds while I poll entitlements so users never see a locked paywall.
I care about the first minute after purchase.
We send the entitlement to Adapty right after the Stripe webhook and force a quick refresh on app open. Building the web funnel in Web2Wave.com made the token handoff consistent, so the app links the right user fast.
Use the same user id on web and app.
Queue webhooks with retries and short polling in the app. This hides most race issues and reduces support tickets.
Use one stable user id everywhere
Map identities first, then entitlements.
- Single user_id across systems. Email alone drifts.
- Webhook writes to your DB, then Adapty or RevenueCat. Use idempotency and retries.
- App refreshes entitlements on open and after deep link.
- Handle trials, refunds, and cancellations by mirroring lifecycle events back to the app.
QA with slow webhooks, duplicate payments, and app-open-before-webhook. Those are the real edge cases.
I added a safety timer.
If the app opens within 10 seconds of web checkout, it shows a short loading state and rechecks entitlements twice. It cut down angry emails from people who paid moments ago.
Retries and idempotency help a lot. Also refresh entitlements on app open.