How to Add Payment Processing to Your React Native App in 2026
Adding payments to a React Native app sounds simple until you realize there's no single "right way." If you sell physical goods, Stripe is the answer. If you sell digital content consumed inside your app, Apple and Google require their own billing APIs and they take up to 30%. Mix the two and you'll either pay double fees or get rejected during App Store review.
This is the practical guide I wish I'd had the first time. We'll cover the decision tree, set up @stripe/stripe-react-native, add Apple Pay and Google Pay, handle subscriptions with react-native-iap or RevenueCat, build a minimal backend, and test it all without spending a dollar.
The Decision Tree
| You sell... | Use this | Fee |
|---|---|---|
| Physical goods | Stripe / Braintree | ~2.9% + $0.30 |
| Real-world services (rides, etc.) | Stripe / Braintree | ~2.9% + $0.30 |
| Digital subscriptions | Apple IAP / Google Play Billing | 15–30% |
| One-time digital unlocks | Apple IAP / Google Play Billing | 15–30% |
Apple's Guideline 3.1.1 is the source of truth here. Don't skip it.
Step 1: Install the Stripe SDK
npm install @stripe/stripe-react-native
cd ios && pod install
Expo SDK 50+ works via the config plugin. You need a development build — Expo Go can't run native payment modules.
Step 2: Wrap Your App in StripeProvider
import { StripeProvider } from '@stripe/stripe-react-native';
export default function App() {
return (
<StripeProvider
publishableKey={process.env.EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY!}
merchantIdentifier="merchant.com.yourapp"
urlScheme="yourapp"
>
<RootNavigator />
</StripeProvider>
);
}
Only the publishable key (pk_...) goes in the client. Your secret key never touches the bundle.
Step 3: Create a PaymentIntent on Your Backend
app.post('/payments/create-intent', async (req, res) => {
const intent = await stripe.paymentIntents.create({
amount: req.body.amount,
currency: req.body.currency,
automatic_payment_methods: { enabled: true },
});
res.json({ clientSecret: intent.client_secret });
});
You can't create PaymentIntents from the client. Stripe blocks it because it would let an attacker manipulate the amount.
Step 4: Present PaymentSheet
const { initPaymentSheet, presentPaymentSheet } = useStripe();
async function checkout() {
const { clientSecret } = await api.createIntent({ amount: 1999 });
await initPaymentSheet({
merchantDisplayName: 'Your Store',
paymentIntentClientSecret: clientSecret,
applePay: { merchantCountryCode: 'US' },
googlePay: { merchantCountryCode: 'US', testEnv: true },
});
const { error } = await presentPaymentSheet();
if (!error) showReceipt();
}
PaymentSheet picks the right local methods automatically — cards in the US, iDEAL in the Netherlands, BLIK in Poland. You don't build any of that.
Apple Pay & Google Pay
Both flow through Stripe, they're not separate gateways. To enable Apple Pay:
- Create a Merchant ID in the Apple Developer portal.
- Generate the processing certificate from Stripe's dashboard.
- Add the
Apple Paycapability in Xcode. - Pass
merchantIdentifiertoStripeProvider.
Google Pay is one config flag plus an AndroidManifest.xml meta-data tag. Conversion lifts 20–30% on iOS when Apple Pay is offered.
Subscriptions: react-native-iap or RevenueCat?
For digital content, you have to use Apple/Google IAP. Two practical paths:
react-native-iap (free, DIY backend):
import { initConnection, requestPurchase, finishTransaction } from 'react-native-iap';
await initConnection();
const purchase = await requestPurchase({ sku: 'pro_monthly' });
// validate receipt on your backend, then:
await finishTransaction({ purchase, isConsumable: false });
RevenueCat (free under ~$2.5K MTR, then 1% — handles all the receipt validation, entitlement, and analytics):
import Purchases from 'react-native-purchases';
await Purchases.configure({ apiKey: 'your_key' });
const offerings = await Purchases.getOfferings();
const { customerInfo } = await Purchases.purchasePackage(
offerings.current.monthly
);
const isPro = customerInfo.entitlements.active['pro'] !== undefined;
Rule of thumb: if you ship subscriptions and don't have a payments engineer, use RevenueCat. The hours you save on receipt validation, server-to-server notifications, and cross-platform entitlement state will pay for it in week one.
Testing Without Real Money
Stripe test cards I use most:
-
4242 4242 4242 4242— success -
4000 0027 6000 3184— 3D Secure challenge -
4000 0000 0000 9995— declined (insufficient funds) -
4000 0000 0000 0341— fails on charge
Apple sandbox subscriptions renew at accelerated cadence (1 month = 5 minutes, up to 6 cycles). You can verify your renewal logic in an afternoon.
Pre-Ship Checklist
- [ ] Successful card charge end-to-end
- [ ] 3D Secure / SCA challenge completes
- [ ] Apple Pay / Google Pay works
- [ ] IAP purchase + restore + refund tested
- [ ] Webhook idempotency (duplicate events don't double-grant)
- [ ] Subscription cancellation eventually revokes access
- [ ] Network drop mid-purchase doesn't lose the receipt
Common Pitfalls
- PCI scope creep — stay in SAQ A by always using PaymentSheet. The moment you build a custom card form, the audit burden explodes.
- SCA failures in Europe — let Stripe pick payment methods automatically; custom flows often skip the 3DS challenge.
- Idempotency keys — pass one on every server write. Retries that create duplicate intents charge the user twice.
-
Currency mismatch — store currency next to amount.
1999is not the same revenue in USD vs JPY.
Wrapping Up
Payments will always require thoughtful backend and compliance work. But the React Native scaffolding around them — paywalls, settings screens, restore-purchase flows — is repetitive plumbing.
If you want to skip the boilerplate, RapidNative generates production-ready React Native + Expo code from a sentence. Describe your paywall, iterate visually, drop in your Stripe and RevenueCat keys, and ship.
What's your stack — Stripe + RevenueCat? Pure react-native-iap? Curious what's working in 2026.
Top comments (0)