TechnicalApril 2026 · 11 min read

Adding Stripe Payments to an AI-Generated React Native App

Stripe is the default way to charge customers inside a React Native app when the rules let you — and the rules let you in most cases. This guide walks through setting up Stripe PaymentSheet in an Expo app exported from ShipNative (or any Expo project), from SDK install to live payments.

Quick path

Install @stripe/stripe-react-native, create a backend endpoint that returns PaymentIntent client secrets, wrap your app in <StripeProvider>, present PaymentSheet on checkout, handle webhooks server-side. 1–2 hours on a clean project.

Stripe or IAP? The 2-second rule

Apple’s rule is simpler than most blog posts make it. Ask: “Is the thing being sold consumed inside my app?”

  • Yes (digital): pro features, unlock levels, subscription tier, in-app coins. Must use IAP.
  • No (physical or out-of-app service): products shipped to the user, booking an Uber ride, paying a marketplace seller, food delivery, event tickets, personal training. Use Stripe.

The 30% tax only applies to digital. Use Stripe whenever you legally can.

Prerequisites

  • Stripe account with a test mode key and a plan to complete the live activation checklist before launch
  • A backend endpoint (Supabase Edge Functions, Expo API Routes, or your own server)
  • Expo project using EAS Build — payments do not run in Expo Go
  • An Apple Merchant ID if you plan to support Apple Pay

Step 1: install and configure

npx expo install @stripe/stripe-react-native

# app.json plugins:
# "plugins": [
#   ["@stripe/stripe-react-native", {
#     "merchantIdentifier": "merchant.com.yourapp",
#     "enableGooglePay": true
#   }]
# ]

# Create a dev build:
eas build --profile development --platform all

Step 2: wrap your app in StripeProvider

// app/_layout.tsx (Expo Router):
import { StripeProvider } from '@stripe/stripe-react-native';
import { Slot } from 'expo-router';

export default function RootLayout() {
  return (
    <StripeProvider
      publishableKey={process.env.EXPO_PUBLIC_STRIPE_PK!}
      merchantIdentifier="merchant.com.yourapp"
    >
      <Slot />
    </StripeProvider>
  );
}

Use the publishable key (pk_test_...). The secret key never appears in the client.

Step 3: backend endpoint to create PaymentIntents

// Supabase Edge Function or Node API:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: Request) {
  const { amount, userId } = await req.json();

  const customer = await stripe.customers.create({
    metadata: { userId },
  });

  const paymentIntent =
    await stripe.paymentIntents.create({
      amount, // cents
      currency: 'usd',
      customer: customer.id,
      automatic_payment_methods: { enabled: true },
    });

  return Response.json({
    clientSecret: paymentIntent.client_secret,
    customerId: customer.id,
    ephemeralKey: (await stripe.ephemeralKeys.create(
      { customer: customer.id },
      { apiVersion: '2024-12-18.acacia' }
    )).secret,
  });
}

Step 4: present PaymentSheet on checkout

import { useStripe } from '@stripe/stripe-react-native';

const { initPaymentSheet, presentPaymentSheet } =
  useStripe();

async function onCheckout() {
  const res = await fetch('/api/payment-intent', {
    method: 'POST',
    body: JSON.stringify({ amount: 2999, userId }),
  });
  const data = await res.json();

  await initPaymentSheet({
    paymentIntentClientSecret: data.clientSecret,
    customerId: data.customerId,
    customerEphemeralKeySecret: data.ephemeralKey,
    merchantDisplayName: 'Your Brand',
    applePay: { merchantCountryCode: 'US' },
    googlePay: {
      merchantCountryCode: 'US',
      testEnv: __DEV__,
    },
  });

  const { error } = await presentPaymentSheet();
  if (!error) {
    // Payment succeeded — navigate to success screen.
  }
}

PaymentSheet handles card entry, Apple Pay, Google Pay, saved cards, and 3D Secure. You write the success handler; everything else is Stripe.

Step 5: webhooks for order reliability

Never trust the client to confirm payment. Stripe sends webhooks to a URL you expose; your backend is the source of truth for order status.

  • payment_intent.succeeded — mark the order paid, trigger fulfillment.
  • payment_intent.payment_failed — notify the customer, allow retry.
  • charge.refunded — update order status.
  • Verify the Stripe signature on every webhook — stripe.webhooks.constructEvent.

Testing

  • Use 4242 4242 4242 4242 as the test card — any future date, any CVC.
  • Test 3D Secure with 4000 0027 6000 3184.
  • Apple Pay sandbox requires a real Apple ID signed into a physical device.
  • Google Pay in test mode uses Stripe test cards on Android devices signed into Google Pay.

Common gotchas

  • Forgetting the merchant identifier in app.json → Apple Pay button never appears.
  • Mixing test and live keys between client and backend. Both must match.
  • Trusting the client to mark orders paid. Always wait for the webhook.
  • Not saving the Stripe Customer ID. Required for saved cards and subscriptions.
  • Forgetting to enable Apple Pay in the Xcode capabilities. Simple to miss; breaks silently.

Faster path: scaffold it in the prompt

Apps generated via ShipNative can include the Stripe client-side plumbing if you describe your monetization in the initial prompt — e.g., “Checkout via Stripe PaymentSheet with Apple Pay.” You still wire up the backend endpoints and webhooks yourself, but the RN-side code ships pre-wired and tested.

Frequently Asked Questions

When should I use Stripe vs Apple in-app purchases?

Physical goods and services consumed outside the app use Stripe (no Apple tax). Digital goods and subscriptions consumed inside the app require IAP (15–30%). Apparel store, coffee subscription box, marketplace transactions — Stripe. Coaching chat inside your app, pro features, ad-free tier — IAP.

Does Stripe work with Expo managed workflow?

Yes. @stripe/stripe-react-native ships a config plugin that works with EAS Build. You cannot test payments inside Expo Go — a dev build is required. Setup end-to-end takes about 1–2 hours.

How do I add Apple Pay and Google Pay?

PaymentSheet includes them automatically if you configure the merchant identifier on iOS (Xcode capability) and enable Google Pay in your Stripe dashboard. You do not write additional code beyond the merchantIdentifier parameter.

Do I need a backend to accept Stripe payments?

Yes. Stripe keys must not live in your app. You need a server endpoint that creates PaymentIntents with your secret key. Supabase Edge Functions, Expo API Routes, Vercel functions, or a simple Node server all work — pick one you can maintain.

How do I handle refunds?

Refunds happen via the Stripe Dashboard or a single API call (stripe.refunds.create). You typically trigger them from an admin web UI, not the mobile app. Webhook events (charge.refunded) can update your order records automatically.

RevenueCat + Expo Setup

When IAP is the right answer instead of Stripe.

See setup →

E-commerce App Without Code

Where Stripe is the default monetization stack.

Read guide →

Ship a real React Native app today

Describe, preview, and export Expo code — free to start.

Build with ShipNative →