Expo RevenueCat plus Superwall Integration
Use this skill to add or repair a modern RevenueCat plus Superwall stack in a React Native Expo app.
What this skill should do
- Choose the correct monetisation architecture before editing code.
- Integrate with the repository's existing app shell, auth layer, and state management.
- Prefer safe, production-ready defaults over the shortest possible demo.
- Leave the user with code changes plus a clear list of remaining dashboard, store, and testing steps.
Critical rules
- Treat this as an Expo development-build integration, not an Expo Go integration.
- Target Expo SDK 53 or newer.
- Target iOS deployment target 15.1 or newer and Android min SDK 23 or newer.
- Use public SDK keys only in the client.
- Configure RevenueCat exactly once.
- Mount Superwall near the app root exactly once.
- Use the same stable, non-guessable, non-PII user identifier in RevenueCat and Superwall when the product has authentication.
- Never use email addresses as RevenueCat or Superwall user IDs.
- Do not call
syncPurchases() on every launch. Use it only for deliberate migration or account-recovery scenarios.
restorePurchases() is user-triggered. Do not hide it inside startup code.
- On Android, ensure the launch mode is
standard or singleTop.
- Prefer a full app restart after Superwall dashboard changes during Expo development.
First actions
Inspect the repository before editing:
package.json
app.json, app.config.js, or app.config.ts
App.tsx or app/_layout.tsx
- any existing auth provider
- any existing purchase, paywall, or entitlement code
Run the validator if Python is available:
python3 scripts/validate_expo_setup.py
- or
python3 scripts/validate_expo_setup.py --project-root /path/to/app
Answer these six preflight questions before choosing code:
- Is the app login-first, login-optional, or guest-first
- Is there existing purchase completion logic already in the repo
- Does Google Play use multiple base plans or offers
- Are App Store Server Notifications, Google server notifications, webhooks, or backend attribution in scope
- Is the entitlement model single-tier or multi-tier
- Does the product need strict account ownership, or easy restore across account confusion
Open only the references you need:
- Core workflow:
references/implementation-playbook.md
- Architecture choice:
references/architecture-decision-tree.md
- Identity and restores:
references/identity-and-restore-behaviour.md
- Android offers:
references/android-base-plans-offers-and-pending.md
- iOS UUID and server notifications:
references/ios-uuid-appaccounttoken-and-server-notifications.md
- Observability and verification:
references/observability-and-entitlement-verification.md
- Test planning:
references/testing-matrix.md
- Dashboard alignment:
references/dashboard-checklist.md
- Failure modes:
references/troubleshooting.md
Architecture choice
Default for most new Expo apps
Choose Architecture A: CustomPurchaseControllerProvider when:
- Superwall is the paywall surface.
- RevenueCat is the purchase and entitlement source of truth.
- The app does not already have its own mature purchase completion pipeline.
- You want the cleanest modern Expo integration.
Use:
references/architecture-decision-tree.md
references/examples/monetization.shared.tsx
references/examples/app.example.tsx
references/examples/expo-router-layout.example.tsx
references/examples/custom-purchase-controller.android-offers.tsx
Use the migration path when the repo already owns purchase completion
Choose Architecture B: purchasesAreCompletedBy / observer-mode migration when:
- The app already finishes transactions itself.
- The user explicitly wants to keep existing IAP code.
- You are layering RevenueCat analytics, entitlements, or dashboards onto an existing billing implementation.
- You must import historical purchases carefully.
Use:
references/architecture-decision-tree.md
references/examples/observer-mode-migration.tsx
references/identity-and-restore-behaviour.md
Shared implementation workflow
1. Audit the repo
Collect these facts before changing code:
- Expo SDK version
- package manager
- router style: Expo Router or plain
App.tsx
- whether
expo-superwall, react-native-purchases, and expo-build-properties are already installed
- current iOS deployment target and Android min SDK
- whether the app has auth
- whether the repo already has RevenueCat, Superwall, StoreKit, Google Play Billing, or
react-native-iap code
- whether the project already ships one-time products in addition to subscriptions
2. Align dashboards before deep code edits
Confirm the conceptual setup first:
- RevenueCat project exists for iOS and Android
- store products exist
- entitlements exist
- offerings exist where needed
- Superwall project exists
- Superwall public keys exist for both platforms
- placements and campaigns exist
- product IDs and entitlement IDs match the intended runtime mapping
Use references/dashboard-checklist.md.
3. Install only the packages you actually need
Base stack:
npx expo install expo-superwall react-native-purchases expo-build-properties
Optional only if the user explicitly wants RevenueCat UI screens such as a customer center:
npx expo install react-native-purchases-ui
Do not add react-native-purchases-ui just because RevenueCat is installed.
4. Update Expo config
Add or repair expo-build-properties and set platform minimums. Preserve the repository's config style and existing plugins.
5. Configure RevenueCat once
- Use the correct public key for the current platform.
- Configure once on startup.
- If the app always requires a known user ID, prefer configuring with that ID instead of creating an anonymous state first.
- If the app allows guests, configure without an App User ID and later call
logIn() when auth resolves.
6. Mount providers once near the root
For Architecture A, the normal order is:
- configure RevenueCat
- mount
CustomPurchaseControllerProvider
- mount
SuperwallProvider
- show
SuperwallLoading
- render the app inside
SuperwallLoaded
- mount one subscription sync component inside the loaded tree
7. Sync RevenueCat entitlements into Superwall
When Superwall is not directly owning purchase state, map RevenueCat entitlements into setSubscriptionStatus.
- Fetch
CustomerInfo on launch or when premium UI opens.
- Subscribe to
addCustomerInfoUpdateListener.
- Map active entitlement IDs into Superwall entitlements.
- Prefer syncing the full entitlement set, not just a boolean.
8. Sync identities deliberately
- Reuse the app's real auth state.
- For login-first apps, prefer configuring RevenueCat with a custom App User ID from the start.
- For guest-first apps, configure anonymously, then on login call
Purchases.logIn(userId) and identify(userId).
- If switching from one known account to another, call
logIn(newUserId) directly. Do not force a pointless logout first.
- Only call
logOut() if the product truly supports an anonymous post-logout state.
See references/identity-and-restore-behaviour.md and references/examples/auth-sync.example.tsx.
9. Register placements from premium entry points
- Use business-action placement names such as
upgrade_pro, remove_limits, or export_pdf.
- Prefer placement-driven gating and dashboard audiences over hard-coded paywall branching.
- Use
getPresentationResult() only when you need to inspect what Superwall would do before presenting.
10. Add observability
- Forward Superwall events into the app's analytics pipeline.
- Keep debug logs enabled in development only.
- Consider checking RevenueCat trusted entitlement verification in high-risk apps.
See references/observability-and-entitlement-verification.md.
11. Test with a matrix, not one happy path
Always verify:
- cold start on iOS and Android
- purchase success
- cancel flow
- pending flow where relevant
- restore
- guest to logged-in transition
- account switch
- reinstall
- dashboard changes after a full restart
- entitlement unlock in UI
- webhook or server-notification identity consistency when relevant
Use references/testing-matrix.md.
What to avoid
- Do not use
expo-superwall/compat for new work.
- Do not promise real Superwall support inside Expo Go.
- Do not call
restorePurchases() automatically during startup.
- Do not call
syncPurchases() on every launch.
- Do not use emails, IDFA, device IDs, or hardcoded strings as billing IDs.
- Do not leave duplicate purchase flows in place.
- Do not ship Android API 21 just because Superwall alone supports it; the combined stack needs Android 23 or newer.
Final answer expectations
When you finish editing a repo, your response should include:
- the files changed
- the chosen architecture and why
- any manual RevenueCat, Superwall, App Store Connect, or Play Console steps still required
- any assumptions about entitlements, product IDs, placements, restore behaviour, or account model
- any migration debt intentionally left in place
- how to run and test the development build
1---2name: expo-revenuecat-superwall-integration3description: Adds, repairs, or migrates a production-grade RevenueCat plus Superwall integration in a React Native Expo app for iOS and Android. Chooses between CustomPurchaseControllerProvider and purchasesAreCompletedBy or observer-mode migration, wires Expo config and development builds, syncs identities and entitlements, handles Android base plans and offers, iOS UUID appAccountToken quirks, restore behaviour, analytics, testing, and troubleshooting. Use when the user asks to add subscriptions, paywalls, RevenueCat, Superwall, entitlements, restore flows, account switching, or monetisation migration in an Expo app. Do not use for bare React Native, RevenueCat-only UI work, or web-only billing.4license: MIT5---6# Expo RevenueCat plus Superwall Integration
7
8Use this skill to add or repair a modern RevenueCat plus Superwall stack in a React Native Expo app.
9
10## What this skill should do
11
12- Choose the correct monetisation architecture before editing code.
13- Integrate with the repository's existing app shell, auth layer, and state management.
14- Prefer safe, production-ready defaults over the shortest possible demo.
15- Leave the user with code changes plus a clear list of remaining dashboard, store, and testing steps.
16
17## Critical rules
18
19- Treat this as an Expo development-build integration, not an Expo Go integration.
20- Target Expo SDK 53 or newer.
21- Target iOS deployment target 15.1 or newer and Android min SDK 23 or newer.
22- Use public SDK keys only in the client.
23- Configure RevenueCat exactly once.
24- Mount Superwall near the app root exactly once.
25- Use the same stable, non-guessable, non-PII user identifier in RevenueCat and Superwall when the product has authentication.
26- Never use email addresses as RevenueCat or Superwall user IDs.
27- Do not call `syncPurchases()` on every launch. Use it only for deliberate migration or account-recovery scenarios.
28- `restorePurchases()` is user-triggered. Do not hide it inside startup code.
29- On Android, ensure the launch mode is `standard` or `singleTop`.
30- Prefer a full app restart after Superwall dashboard changes during Expo development.
31
32## First actions
33
341. Inspect the repository before editing:
35 - `package.json`
36 - `app.json`, `app.config.js`, or `app.config.ts`
37 - `App.tsx` or `app/_layout.tsx`
38 - any existing auth provider
39 - any existing purchase, paywall, or entitlement code
40
412. Run the validator if Python is available:
42 - `python3 scripts/validate_expo_setup.py`
43 - or `python3 scripts/validate_expo_setup.py --project-root /path/to/app`
44
453. Answer these six preflight questions before choosing code:
46 - Is the app login-first, login-optional, or guest-first
47 - Is there existing purchase completion logic already in the repo
48 - Does Google Play use multiple base plans or offers
49 - Are App Store Server Notifications, Google server notifications, webhooks, or backend attribution in scope
50 - Is the entitlement model single-tier or multi-tier
51 - Does the product need strict account ownership, or easy restore across account confusion
52
534. Open only the references you need:
54 - Core workflow: `references/implementation-playbook.md`
55 - Architecture choice: `references/architecture-decision-tree.md`
56 - Identity and restores: `references/identity-and-restore-behaviour.md`
57 - Android offers: `references/android-base-plans-offers-and-pending.md`
58 - iOS UUID and server notifications: `references/ios-uuid-appaccounttoken-and-server-notifications.md`
59 - Observability and verification: `references/observability-and-entitlement-verification.md`
60 - Test planning: `references/testing-matrix.md`
61 - Dashboard alignment: `references/dashboard-checklist.md`
62 - Failure modes: `references/troubleshooting.md`
63
64## Architecture choice
65
66### Default for most new Expo apps
67
68Choose **Architecture A: CustomPurchaseControllerProvider** when:
69
70- Superwall is the paywall surface.
71- RevenueCat is the purchase and entitlement source of truth.
72- The app does not already have its own mature purchase completion pipeline.
73- You want the cleanest modern Expo integration.
74
75Use:
76- `references/architecture-decision-tree.md`
77- `references/examples/monetization.shared.tsx`
78- `references/examples/app.example.tsx`
79- `references/examples/expo-router-layout.example.tsx`
80- `references/examples/custom-purchase-controller.android-offers.tsx`
81
82### Use the migration path when the repo already owns purchase completion
83
84Choose **Architecture B: purchasesAreCompletedBy / observer-mode migration** when:
85
86- The app already finishes transactions itself.
87- The user explicitly wants to keep existing IAP code.
88- You are layering RevenueCat analytics, entitlements, or dashboards onto an existing billing implementation.
89- You must import historical purchases carefully.
90
91Use:
92- `references/architecture-decision-tree.md`
93- `references/examples/observer-mode-migration.tsx`
94- `references/identity-and-restore-behaviour.md`
95
96## Shared implementation workflow
97
98### 1. Audit the repo
99
100Collect these facts before changing code:
101
102- Expo SDK version
103- package manager
104- router style: Expo Router or plain `App.tsx`
105- whether `expo-superwall`, `react-native-purchases`, and `expo-build-properties` are already installed
106- current iOS deployment target and Android min SDK
107- whether the app has auth
108- whether the repo already has RevenueCat, Superwall, StoreKit, Google Play Billing, or `react-native-iap` code
109- whether the project already ships one-time products in addition to subscriptions
110
111### 2. Align dashboards before deep code edits
112
113Confirm the conceptual setup first:
114
115- RevenueCat project exists for iOS and Android
116- store products exist
117- entitlements exist
118- offerings exist where needed
119- Superwall project exists
120- Superwall public keys exist for both platforms
121- placements and campaigns exist
122- product IDs and entitlement IDs match the intended runtime mapping
123
124Use `references/dashboard-checklist.md`.
125
126### 3. Install only the packages you actually need
127
128Base stack:
129
130- `npx expo install expo-superwall react-native-purchases expo-build-properties`
131
132Optional only if the user explicitly wants RevenueCat UI screens such as a customer center:
133
134- `npx expo install react-native-purchases-ui`
135
136Do not add `react-native-purchases-ui` just because RevenueCat is installed.
137
138### 4. Update Expo config
139
140Add or repair `expo-build-properties` and set platform minimums. Preserve the repository's config style and existing plugins.
141
142### 5. Configure RevenueCat once
143
144- Use the correct public key for the current platform.
145- Configure once on startup.
146- If the app always requires a known user ID, prefer configuring with that ID instead of creating an anonymous state first.
147- If the app allows guests, configure without an App User ID and later call `logIn()` when auth resolves.
148
149### 6. Mount providers once near the root
150
151For Architecture A, the normal order is:
152
1531. configure RevenueCat
1542. mount `CustomPurchaseControllerProvider`
1553. mount `SuperwallProvider`
1564. show `SuperwallLoading`
1575. render the app inside `SuperwallLoaded`
1586. mount one subscription sync component inside the loaded tree
159
160### 7. Sync RevenueCat entitlements into Superwall
161
162When Superwall is not directly owning purchase state, map RevenueCat entitlements into `setSubscriptionStatus`.
163
164- Fetch `CustomerInfo` on launch or when premium UI opens.
165- Subscribe to `addCustomerInfoUpdateListener`.
166- Map active entitlement IDs into Superwall entitlements.
167- Prefer syncing the full entitlement set, not just a boolean.
168
169### 8. Sync identities deliberately
170
171- Reuse the app's real auth state.
172- For login-first apps, prefer configuring RevenueCat with a custom App User ID from the start.
173- For guest-first apps, configure anonymously, then on login call `Purchases.logIn(userId)` and `identify(userId)`.
174- If switching from one known account to another, call `logIn(newUserId)` directly. Do not force a pointless logout first.
175- Only call `logOut()` if the product truly supports an anonymous post-logout state.
176
177See `references/identity-and-restore-behaviour.md` and `references/examples/auth-sync.example.tsx`.
178
179### 9. Register placements from premium entry points
180
181- Use business-action placement names such as `upgrade_pro`, `remove_limits`, or `export_pdf`.
182- Prefer placement-driven gating and dashboard audiences over hard-coded paywall branching.
183- Use `getPresentationResult()` only when you need to inspect what Superwall would do before presenting.
184
185### 10. Add observability
186
187- Forward Superwall events into the app's analytics pipeline.
188- Keep debug logs enabled in development only.
189- Consider checking RevenueCat trusted entitlement verification in high-risk apps.
190
191See `references/observability-and-entitlement-verification.md`.
192
193### 11. Test with a matrix, not one happy path
194
195Always verify:
196
197- cold start on iOS and Android
198- purchase success
199- cancel flow
200- pending flow where relevant
201- restore
202- guest to logged-in transition
203- account switch
204- reinstall
205- dashboard changes after a full restart
206- entitlement unlock in UI
207- webhook or server-notification identity consistency when relevant
208
209Use `references/testing-matrix.md`.
210
211## What to avoid
212
213- Do not use `expo-superwall/compat` for new work.
214- Do not promise real Superwall support inside Expo Go.
215- Do not call `restorePurchases()` automatically during startup.
216- Do not call `syncPurchases()` on every launch.
217- Do not use emails, IDFA, device IDs, or hardcoded strings as billing IDs.
218- Do not leave duplicate purchase flows in place.
219- Do not ship Android API 21 just because Superwall alone supports it; the combined stack needs Android 23 or newer.
220
221## Final answer expectations
222
223When you finish editing a repo, your response should include:
224
225- the files changed
226- the chosen architecture and why
227- any manual RevenueCat, Superwall, App Store Connect, or Play Console steps still required
228- any assumptions about entitlements, product IDs, placements, restore behaviour, or account model
229- any migration debt intentionally left in place
230- how to run and test the development build