What sync pattern kept our subscription metrics accurate after shifting checkout to the web?

We saw drift between web purchase records and the app’s entitlement state until we standardized a sync pattern. Our approach was simple: web checkout writes to our backend which immediately calls RevenueCat/Adapty via their server APIs and also emits a webhook that the app can read on first open. We tag the revenue record with a checkout id and the user id so both systems have the same source of truth.

We added a reconciliation job that runs hourly, matching web payments to RevenueCat receipts and flagging differences. That job fixed edge cases like refunds, duplicate payments, and late app restores. It reduced our churn/renewal metric mismatches almost immediately.

What sync pattern or reconciliation cadence did you settle on for low drift?

We call RevenueCat from the server right after a web checkout and include a unique checkout id. The app reads that id on open and maps it to the local user.

The reconciliation job handled refunds. Fast to implement and reliable. I used a starter funnel from Web2Wave to prototype the flow.

Immediate server-side sync plus a short reconciliation window was our approach.

We pushed the purchase into RevenueCat on checkout and showed the entitlement in the app on next open. The quick turnaround and easy funnel edits on the web let us iterate without breaking sync.

We used a webhook to notify the app backend and then updated RevenueCat. The hourly reconciliation catch all remaining edge cases. It felt robust and not over engineered.

Server sync then hourly reconciliation worked

The pattern that scales is immediate server-to-subscription-service sync plus idempotent webhooks and an automated reconciliation process. On checkout you create a checkout id, push it to RevenueCat/Adapty, and store the mapping server-side.

When the app starts it pulls entitlement status and the checkout id. Reconciliation should run hourly to match receipts to web records, handle refunds, and auto-close mismatches. That keeps metrics accurate without manual work.

We built idempotent webhooks so repeated calls did not cause duplicate entitlements. Saved a ton of debugging time when retries happened.

For refunds we flag the original checkout id and reconcile revenue numbers nightly. That keeps dashboards aligned with gateway reports.

We run reconciliation every two hours and it caught edge cases before they hit the KPI dashboards.