React Native In-App Purchases: RevenueCat vs expo-iap vs react-native-iap
March 2026
If you're adding in-app purchases to a React Native app, use RevenueCat. I've tried all three major approaches across multiple apps, and RevenueCat (through the react-native-purchases SDK) gives you the best balance of simplicity, reliability, and cost. It's free until you're making $2,500/month in revenue, and by that point you won't mind paying.
That said, every approach has trade-offs. Here's the full breakdown.
Table of Contents
How Subscriptions Actually Work
Before picking a library, you need to understand the actual flow of money and data in mobile subscriptions. It's not as simple as "user pays, app unlocks."
Here's what happens step by step:
1. User taps "Subscribe" in your app
2. Your app calls StoreKit (iOS) or Google Play Billing (Android)
3. Apple/Google shows their native payment sheet
4. User authenticates (Face ID, fingerprint, password)
5. Apple/Google processes the payment
6. Store returns a receipt/token to your app
7. Your app sends the receipt to your backend for validation
8. Backend verifies the receipt with Apple/Google servers
9. Backend grants an "entitlement" to the user
10. App checks entitlements to unlock features
The critical part most developers miss is steps 7-9. You cannot trust the client. A jailbroken iPhone or rooted Android device can fake purchase receipts. You need server-side receipt validation.
This is where the three approaches diverge dramatically:
- RevenueCat handles steps 7-10 entirely for you. Their servers validate receipts, track entitlements, and send you webhooks when subscription status changes.
- expo-iap gives you the receipt but you need to build your own validation server.
- react-native-iap gives you the raw receipt and full control, but you build everything yourself.
Subscription lifecycle events add more complexity. Users can cancel, pause, get refunds, have billing issues, switch plans, or let subscriptions lapse and renew. Each of these triggers different events from Apple and Google, and your app needs to handle all of them correctly. RevenueCat normalizes all of this into a single entitlement check.
RevenueCat (react-native-purchases)
RevenueCat is a managed backend for in-app purchases. The react-native-purchases SDK has over 210 monthly searches for good reason -- it abstracts away the worst parts of mobile payments while giving you a clean API.
Why I use it
I integrated RevenueCat into AIVidly (which I later sold on Flippa) and into the Ship React Native boilerplate. Here's what made me choose it:
Setup takes about 30 minutes. You configure your products in App Store Connect and Google Play Console, add them to the RevenueCat dashboard, and install the SDK. Compare that to the days I spent debugging StoreKit receipt validation when I tried going the manual route.
import Purchases from 'react-native-purchases';
// Initialize once at app start
Purchases.configure({ apiKey: 'your_revenucat_api_key' });
// Check if user has premium access
const customerInfo = await Purchases.getCustomerInfo();
const isPremium = customerInfo.entitlements.active['premium'] !== undefined;
// Make a purchase
const offerings = await Purchases.getOfferings();
const monthly = offerings.current?.monthly;
if (monthly) {
await Purchases.purchasePackage(monthly);
}
Receipt validation is automatic. Every purchase is validated server-side. You never touch Apple or Google's validation endpoints.
Webhooks notify your backend. When a subscription renews, cancels, or has a billing issue, RevenueCat sends a webhook to your server. In Ship React Native, these webhooks hit a Convex HTTP endpoint that updates the user's entitlements in real time.
Cross-platform entitlements work out of the box. If a user subscribes on iOS and logs in on Android, their subscription follows them. This is surprisingly hard to build yourself.
Analytics show you real metrics. MRR, churn rate, trial conversion -- you get a dashboard without building anything.
Pricing
Free up to $2,500 in monthly tracked revenue (MTR). After that, 0.8% of MTR on the Starter plan. For context, at $10,000 MTR you'd pay $80/month to RevenueCat. That's a good deal for not having to maintain receipt validation infrastructure.
expo-iap
expo-iap is Expo's built-in module for in-app purchases. With around 140 monthly searches, it's becoming a popular choice for Expo developers who want to stay within the Expo ecosystem.
When it makes sense
If you're building a straightforward app with one or two subscription tiers and you're already all-in on Expo, expo-iap reduces your dependency count. It works with EAS Build and doesn't require a config plugin from a third party.
import { useIAP } from 'expo-iap';
const { connected, subscriptions, getSubscriptions, requestSubscription } = useIAP();
// Fetch available subscriptions
await getSubscriptions(['monthly_premium', 'yearly_premium']);
// Purchase
await requestSubscription({ sku: 'monthly_premium' });
The trade-offs
You need to build your own receipt validation. This is the big one. expo-iap gives you the purchase receipt, but validating it against Apple and Google's servers is on you. That means writing and maintaining server-side code that handles the different receipt formats, validation endpoints, and edge cases for both platforms.
No built-in analytics. You won't know your MRR, churn rate, or trial conversion without building or integrating something else.
No webhook system. When a subscription renews or cancels, you need to poll for status or build your own server-to-server notification handling with Apple and Google.
No A/B testing for paywalls. You can't test different pricing or paywall designs without building that infrastructure.
expo-iap is fine for a simple app with basic subscriptions where you don't need detailed analytics or server-side subscription management. But the moment you need any of those features, you'll end up building what RevenueCat gives you for free.
react-native-iap
react-native-iap is the lowest-level option. It's a direct bridge to StoreKit 2 on iOS and Google Play Billing Library v7 on Android. It gives you the most control and the most work.
When it makes sense
If you have specific requirements that RevenueCat doesn't support, or you're building a payment flow that's highly custom, react-native-iap gives you direct access to the native APIs. Some developers also prefer it for ideological reasons -- no third-party dependency sitting between you and the stores.
import {
initConnection,
getSubscriptions,
requestSubscription,
finishTransaction,
} from 'react-native-iap';
await initConnection();
const subscriptions = await getSubscriptions({
skus: ['monthly_premium', 'yearly_premium'],
});
// Purchase
const purchase = await requestSubscription({
sku: 'monthly_premium',
});
// You MUST finish the transaction
await finishTransaction({ purchase });
The trade-offs
Everything is manual. Receipt validation, entitlement management, subscription lifecycle tracking, cross-platform sync, analytics -- you're building all of it.
Platform differences are your problem. StoreKit 2 and Google Play Billing v7 have different APIs, different receipt formats, and different lifecycle events. react-native-iap abstracts the API calls, but the business logic divergence is on you.
Finishing transactions is critical. If you don't call finishTransaction, Apple and Google will refund the purchase. I've seen developers lose revenue because of this.
I used react-native-iap in an earlier app before switching to RevenueCat. The amount of server-side code I had to write and maintain for receipt validation alone made the switch obvious.
Comparison Table
| Feature | RevenueCat | expo-iap | react-native-iap |
|---|---|---|---|
| Receipt validation | Server-side, automatic | Manual (build your own) | Manual (build your own) |
| Subscription analytics | Built-in dashboard | None | None |
| A/B testing paywalls | Yes (Paywalls feature) | No | No |
| Webhook support | Built-in, both platforms | No | No |
| Free tier | Up to $2,500 MTR | Free (open source) | Free (open source) |
| Setup complexity | Low (30 min) | Medium (1-2 hours) | High (days with backend) |
| Cross-platform entitlements | Automatic | Manual | Manual |
| StoreKit 2 support | Yes | Yes | Yes |
| Google Play Billing v7 | Yes | Yes | Yes |
| Server-side subscription status | Yes | Build your own | Build your own |
| Restore purchases | Handled automatically | Manual implementation | Manual implementation |
For indie developers and small teams, RevenueCat is the clear choice. You trade a small percentage of revenue (after $2,500/month) for not having to build and maintain payment infrastructure.
Credit Systems: Beyond Pure Subscriptions
If you're building an AI-powered app, pure subscriptions might not be the right model. When each API call costs you money (OpenAI, Claude, image generation), unlimited access on a flat subscription can bankrupt you.
This is why I built a credit system into Ship React Native, and it's what I used in AIVidly.
How it works
The model is simple: subscriptions and one-time purchases give users credits, and features consume credits.
User subscribes to Pro plan ($9.99/month)
-> RevenueCat webhook fires
-> Convex backend receives webhook
-> Backend adds 500 credits to user account
User generates an AI video summary
-> App checks: does user have enough credits?
-> If yes: deduct 10 credits, process request
-> If no: show upgrade prompt
Why credits work for AI apps
Cost control. You know exactly how much each credit costs you on the backend. If an AI call costs $0.02 and you charge 10 credits for it, you can price your subscription to maintain margins.
Flexible pricing. You can offer different tiers with different credit amounts. Power users buy more credits. Casual users stay on a smaller plan.
One-time purchase upsells. Users who run out of credits mid-month can buy a credit pack as a one-time purchase. In AIVidly, credit pack purchases accounted for about 20% of total revenue.
Implementation with RevenueCat
In Ship React Native, the credit system works like this:
// Convex mutation: add credits on purchase
export const addCredits = mutation({
args: { userId: v.id('users'), amount: v.number() },
handler: async (ctx, { userId, amount }) => {
const user = await ctx.db.get(userId);
await ctx.db.patch(userId, {
credits: (user?.credits ?? 0) + amount,
});
},
});
// Convex mutation: consume credits
export const useCredits = mutation({
args: { userId: v.id('users'), amount: v.number() },
handler: async (ctx, { userId, amount }) => {
const user = await ctx.db.get(userId);
const current = user?.credits ?? 0;
if (current < amount) throw new Error('Insufficient credits');
await ctx.db.patch(userId, { credits: current - amount });
},
});
The beauty of this approach is that RevenueCat handles all the payment complexity, and your backend just manages a number. Credits go up on purchase, credits go down on usage.
Real Costs of In-App Purchases
Here's what actually happens to money from a $9.99/month subscription:
Apple and Google's cut:
- Year 1: 30% commission. Your $9.99 becomes $6.99.
- Year 2+: 15% commission (Small Business Program / reduced rate for subscriptions after 12 months). Your $9.99 becomes $8.49.
- If you qualify for Apple's Small Business Program (under $1M annual revenue), you get 15% from day one.
RevenueCat's cut (if over $2,500 MTR):
- 0.8% of tracked revenue on the Starter plan.
- On $9.99, that's about $0.08 per subscription per month.
Your actual take on a $9.99 subscription:
- With 15% store commission + RevenueCat: ~$8.41
- With 30% store commission + RevenueCat: ~$6.91
- Without RevenueCat (under $2,500 MTR): $8.49 or $6.99
Taxes:
Apple and Google handle sales tax and VAT collection in most regions. This is one of the major benefits of using in-app purchases over Stripe or other direct payment processors for mobile apps.
The real cost calculation for AI apps:
If your subscription gives 500 credits and each credit costs $0.002 in API fees, that's $1.00 in costs per subscriber per month. On a $9.99 plan with 15% commission, your net margin is about $7.49 per subscriber. That's healthy. If your API costs creep up to $0.01 per credit, you're spending $5.00 and your margin drops to $3.49. Watch your costs.
Common Mistakes That Will Cost You Money
I've made most of these. Learn from my mistakes instead.
Not handling restore purchases
Apple requires a "Restore Purchases" button. If you don't have one, your app will get rejected. But beyond App Review, users switch phones, reinstall apps, and expect their purchases to carry over.
With RevenueCat, restore is one line:
const customerInfo = await Purchases.restorePurchases();
With expo-iap or react-native-iap, you need to handle the restore flow, re-validate receipts, and update your backend. I've seen apps where restore was "implemented" but silently failed, leading to angry 1-star reviews.
Not testing in sandbox
Apple and Google both provide sandbox environments for testing purchases. If you're testing with real money, you're doing it wrong. Sandbox subscriptions renew on accelerated timelines (a monthly subscription renews every 5 minutes in Apple's sandbox), so you can test the full lifecycle quickly.
Set up sandbox test accounts on both platforms before you write a single line of purchase code.
Hardcoding product IDs
Don't embed product IDs directly in your app code. If you need to change pricing, add a new tier, or run a promotion, you don't want to ship an app update.
RevenueCat solves this with "Offerings" -- you configure products server-side and the app fetches the current offering dynamically. With expo-iap or react-native-iap, you should fetch product IDs from your backend or a remote config service.
Not handling subscription status changes
A user's subscription status can change at any time: they cancel, get a refund, have a billing issue, or their payment method expires. Your app needs to handle these cases gracefully.
The worst version of this bug: a user cancels their subscription, your app doesn't notice, and they keep getting premium features for free. Or worse, you cut off a paying user because your status check has a bug.
RevenueCat's getCustomerInfo() always returns the current entitlement state. Call it on app launch, on app foreground, and before gating premium features.
Forgetting transaction finishing on Android
On Android with react-native-iap, you must acknowledge purchases within 3 days or Google will automatically refund them. This is one of those bugs that doesn't show up in testing but silently costs you revenue in production.
How to Monetize a React Native App?
There are five proven monetization strategies for React Native apps. Most successful apps combine two or three:
1. Subscriptions (recurring revenue). Best for apps that provide ongoing value: AI tools, productivity apps, content apps. This is the most predictable revenue model. Start with monthly and annual plans. Offer a free trial -- 3 or 7 days works well for most categories.
2. One-time purchases. Best for utility apps or premium unlocks. The user pays once, gets access forever. Lower revenue ceiling but simpler to manage. Works well as a complement to subscriptions (credit packs, premium features).
3. Freemium with limits. Give core features for free, charge for advanced features or higher usage limits. This is effectively the credit system model: free users get 50 credits/month, paid users get 500.
4. Ads (AdMob, AppLovin). Works for apps with high engagement and large user bases. Poor fit for utility apps or anything where ads would damage the user experience. Revenue per user is low -- you need volume.
5. Paid upfront. Charge for the app itself on the store. This is declining in popularity because users expect to try before they buy. It still works for niche professional tools.
For most indie developers building React Native apps in 2026, I recommend subscriptions with a free trial plus optional one-time credit packs. This is what I use in my apps and what Ship React Native is built around.
How Much Can a 1000 Downloads App Make?
It depends entirely on your conversion rate and pricing, but here are realistic numbers from my experience.
Typical conversion rates for indie apps:
- Free to trial: 5-15% (depends heavily on your paywall and app category)
- Trial to paid: 30-60% (depends on trial length and app value)
- Overall free to paid: 2-8%
Scenario: 1000 downloads, $9.99/month subscription
At a 3% conversion rate (conservative):
- 30 paying subscribers
- 30 x $9.99 = $299.70/month gross
- After Apple's 15% cut: ~$254.75/month
- Annual: ~$3,057
At a 6% conversion rate (good paywall, strong value prop):
- 60 paying subscribers
- 60 x $9.99 = $599.40/month gross
- After Apple's 15% cut: ~$509.49/month
- Annual: ~$6,114
These numbers assume all 1000 downloads happen in month one and users stick around. In reality, downloads accumulate over time and churn reduces your active subscriber base. A realistic monthly churn rate for indie apps is 8-15%.
How to improve these numbers:
- Optimize your paywall (A/B test with RevenueCat)
- Offer an annual plan at a discount (better retention)
- Add credit packs for one-time purchase revenue
- Focus on retention: push notifications, email, app updates
The biggest lever isn't downloads -- it's conversion rate and retention. An app with 500 downloads and 8% conversion makes more than an app with 2000 downloads and 1% conversion.
Ship React Native: Monetization Pre-Built
I built Ship React Native to be the boilerplate I wished I had when I started monetizing my apps. Every monetization component I described in this post is already set up and working:
- RevenueCat integration with react-native-purchases, configured and ready to go
- Subscriptions (monthly and annual) with free trial support
- One-time purchases for credit packs
- Paywall UI components that you can customize to match your app
- Credit system with Convex backend, webhook handling, and credit balance tracking
- Restore purchases handled correctly on both platforms
- Entitlement checks throughout the app with a simple hook
You configure your product IDs and RevenueCat API key, and the entire purchase flow works. No days lost to StoreKit debugging. No webhook infrastructure to build. No receipt validation headaches.
If you're building a React Native app and want to start making money from it, check out Ship React Native. It's everything I've learned from monetizing AIVidly, Newsletterytics, and 10+ other apps, packaged into code you can ship today.