Migrate from RevenueCat
This guide covers the practical steps to move an app off RevenueCat onto stubkit without breaking existing subscribers.
What stubkit does differently
- Offering model. RC has
Offering → Package → Product; stubkit hasOffering → Product(with anentitlementfield on each product). - Cross-platform user. RC uses
appUserID; stubkit usesuser_idforwarded viaappAccountToken,obfuscatedAccountId, or Stripe metadata. - Entitlement check. RC:
Purchases.shared.customerInfo(). stubkit:GET /v1/entitlement/{app_id}/{user_id}or the local cache.
Migration phases
Phase 1 — parallel run (2 weeks)
- Register the app in stubkit dashboard.
- Upload the same Apple
.p8+ Google service account JSON that RC has. - Generate a publishable key.
- On new purchases only, call
POST /v1/purchaseswith the StoreKit JWS or purchaseToken alongside RC'ssyncPurchases.
Phase 2 — backfill
Two options:
- Let renewals flow in. Any future renewal webhook goes through stubkit's full pipeline. Most subs renew within 30 days.
- Explicit backfill. Export RC customers and either seed entitlements via
POST /v1/admin/grants, or replay transactions throughPOST /v1/purchases.
Phase 3 — cut over
- Point StoreKit/Play webhook URLs at stubkit:
https://api.stubkit.com/webhooks/apple,/webhooks/google,/webhooks/stripe. - Remove
Purchases.configurefrom the app. - Switch entitlement reads to
StubkitPurchases/ the REST API. - Replace RC
Offerings/Paywallsviews with stubkit paywall components.
Phase 4 — decommission
Ship an app update without RC, wait for >95% rollout, remove RC's webhook URL, cancel the RC subscription.
Gotchas
- UUID requirement on iOS.
appAccountTokenmust be a UUID. If your user id isn't a UUID, hash it first. - Historical charts reset. MRR/LTV history starts fresh with stubkit. If you need continuity, import a historical dataset via admin endpoints.
- Sandbox separation. Use
pk_test_/sk_test_keys so sandbox purchases don't pollute production MRR.