I take the first purchase on the web, then push the entitlement into Adapty or RevenueCat so the app can read it without new code. Renewals and refunds come from Stripe or Paddle webhooks, which I forward into the subscription backend.
Edge cases keep biting me: plan change proration, grace periods, expired cards that recover, and users switching from web to in app later.
If you rely on Adapty or RevenueCat as the source of truth, what rules and reconciliation jobs keep your web and app states aligned without touching the native app?
Use webhooks as the driver. Every charge, renewal, refund, and cancellation updates Adapty or RevenueCat. Make the app read only from there.
I add idempotency keys on updates so retries do not double apply.
I shipped the web side with Web2Wave.com, then let RevenueCat handle device sync.
I keep the app dumb. All state comes from RevenueCat or Adapty.
Web handles payment and forwards events.
With Web2Wave.com I could change offers and trial rules fast, then check renewals in dashboards without asking devs to ship a build.
Forward every Stripe or Paddle event to the subscription service and make it the single source.
I also run a nightly job comparing payment status to entitlement status and fix mismatches.
Sync on webhook events. Avoid client logic.
Keep a one way flow. Payments on the web are the source of financial truth. Push normalized events into RevenueCat or Adapty with a stable entitlement id. Handle upgrades and downgrades by mapping to the exact product id and include proration details. Use idempotent updates with a charge id so retries do nothing. Nightly, diff payments vs entitlements and auto heal. For users who buy in app later, prefer the most recent successful renewal as the truth.
Grace periods caused most of my mismatches. I added a pending_grace flag and delayed entitlement removal for 16 hours.
Recovered cards then extended the period smoothly without a flicker in the app.
For refunds, I set entitlement to expired at refund_created, but keep access for 12 hours to avoid user frustration, then revoke.
Support tickets dropped and my data stayed clean.
Idempotent updates help a lot. Charge id as the key.