Quick path
Install react-native-purchases, configure a dev build via EAS, create products in App Store Connect and Play Console, link them in RevenueCat, add a paywall component, and test in sandbox. Expect 2–3 hours end-to-end on a clean project.
Why RevenueCat (and not DIY)
You can call StoreKit and Play Billing directly. You probably should not. RevenueCat abstracts away the parts that burn time and cause production bugs:
- Receipt validation (server-side, already hardened)
- Unified entitlements across iOS and Android (one user, one subscription state)
- Free trials, intro offers, and promotional codes out of the box
- Webhooks for granting access on your backend
- Analytics that matter — MRR, churn, LTV — without you wiring them
The free tier covers tracked revenue up to $10k/month. You pay 1% above that. For almost every indie app, the time saved is worth more than the percentage.
Prerequisites
- An Apple Developer account ($99/year) and a paid tax/banking profile in App Store Connect
- A Google Play Developer account ($25 one-time) with merchant account set up
- An Expo project using EAS Build (Expo Go does not support in-app purchases)
- A RevenueCat account (free to sign up)
- At least one sandbox test account per platform
Step 1: Install and configure the SDK
npx expo install react-native-purchases # Add to app.json plugins: # "plugins": ["react-native-purchases"] # Build a dev client: eas build --profile development --platform ios eas build --profile development --platform android
Install the dev client on your device. From now on, run npx expo start --dev-client instead of Expo Go.
Step 2: Create products in App Store Connect and Play Console
Do this before touching any code. For a standard subscription app:
- App Store Connect: Features → In-App Purchases → Create subscription group → Add products (monthly, annual). Set prices, localize for each region.
- Play Console: Monetize → Subscriptions → Create base plan for each SKU. Match the product IDs you used on iOS.
- Keep product IDs identical across platforms: e.g.,
pro_monthlyandpro_annual.
Step 3: Wire products into RevenueCat
In the RevenueCat dashboard:
- Create a project, add both iOS and Android apps with bundle IDs.
- Upload your App Store Connect API key and Play Service Account JSON for receipt validation.
- Import products using the same IDs you created in both stores.
- Create an Offering (e.g.,
default) with two Packages: monthly and annual. - Create one Entitlement (e.g.,
pro) and attach both products to it.
Step 4: Initialize and render a paywall
import Purchases from 'react-native-purchases';
// In your app entry (e.g., _layout.tsx):
Purchases.configure({
apiKey: Platform.OS === 'ios' ? IOS_KEY : ANDROID_KEY,
});
// Check if user has entitlement:
const info = await Purchases.getCustomerInfo();
const isPro = info.entitlements.active.pro !== undefined;
// Show paywall:
const offerings = await Purchases.getOfferings();
const pkg = offerings.current.monthly;
await Purchases.purchasePackage(pkg);For a ready-made UI, RevenueCat ships react-native-purchases-ui — paywall components you configure in the dashboard and render with a single component. This is the fastest path for most apps.
Step 5: Test in sandbox
Test before you ship — subscription bugs are the most expensive bugs.
- iOS: App Store Connect → Users and Access → Sandbox Testers → create a fresh tester. On device, sign out of real Apple ID first. Do not sign into sandbox manually — let the app prompt you.
- Android: Play Console → Setup → License testing → add your Gmail. Ship an internal testing build and purchase as the tester account.
- Sandbox time-scaled renewals: iOS renews a monthly subscription every 5 minutes in sandbox. Useful for testing renewal logic quickly.
Common gotchas
- Bundle ID mismatch: The bundle ID in App Store Connect must match your Expo app exactly. A one-character typo sinks every build.
- Pending transactions on resume: Always call
Purchases.getCustomerInfo()on app resume. Users sometimes complete purchases outside your app flow. - Restore Purchases button: Apple requires it. Add a visible Restore Purchases option in settings — a review rejection trigger if missing.
- Trials cancel without refunding: Apple deducts the trial from paid time if the user subscribes after canceling. Explain this clearly in your paywall copy.
Generating a paywall-ready app faster
If you generate your app with ShipNative, a paywall screen and RevenueCat integration can be scaffolded in the initial prompt — just describe your monetization model in the description. You still do the App Store Connect and Play Console work yourself, but the in-app plumbing arrives wired.