Journal

Stripe Integration in Expo Apps With AI: The Right Shape

Sheet in the app, intents on the server, truth in the webhook. Here is the Stripe-in-Expo architecture to hand your agent.

Stripe Integration in Expo Apps With AI: The Right Shape: a glossy App Store icon on a blue, pink and orange gradient with bubbles

TL;DR

A direct Stripe integration in an Expo app is three pieces in three places: the native payment sheet in the app, payment intents created on your server with the secret key, and a webhook that is the only source of truth for fulfillment. It requires a development build, the Stripe SDK is a native module Expo Go cannot run, and it is only lawful for physical goods and real-world services, since digital in-app features must use Apple's IAP. Hand your agent that architecture explicitly, rehearse decline and 3D Secure test cards, audit the bundle for the secret key, and start the checkout screen from a free VP0 design extended from its source page.

What a Stripe integration in an Expo app involves

A direct Stripe integration in an Expo app is three pieces in three places: the native payment sheet in the app, a payment intent created on your server, and a webhook that tells your server what actually happened. The client side uses Stripe’s React Native SDK, which presents the system-quality sheet with cards, Apple Pay, and saved methods; the SDK is the ecosystem standard, @stripe/stripe-react-native pulls roughly 341,482 weekly npm downloads, and it is genuinely good, which is why “integrate Stripe” is mostly server and wiring work rather than UI work.

The mental model that keeps agents and humans out of trouble: the app never decides anything about money. It displays the sheet, the server creates and confirms the intent, and the webhook is the source of truth for what to fulfill. Every Stripe integration that goes wrong in an AI-built app goes wrong by violating one of those three sentences, usually all of them at once.

Why Expo Go fails, and what a development build is

The Stripe SDK is a native module, and Expo Go only contains the native modules Expo ships in the sandbox app, so any project that adds Stripe stops running in Expo Go. The fix is a development build: your own version of the Expo Go idea, compiled with your project’s native dependencies inside. It is a one-time setup cost, not an ejection, the workflow stays Expo, and rebuilding is only needed when native dependencies change; day-to-day JavaScript work keeps the same fast refresh it always had.

This is the single most common wall vibe coders hit with payments, and it looks like a Stripe bug when it is a runtime fact. The agent adds the SDK, the QR code still opens Expo Go, the app crashes or the sheet never appears, and an afternoon disappears. State it in the prompt up front, this project uses a development build, not Expo Go, and the whole class of confusion never starts. The same wall, met one builder at a time, is what the Expo subscriptions guide hits with RevenueCat, because every native payment SDK shares it.

The pieces, and where each one runs

PieceWhere it livesIts one job
Payment sheetThe app, via the native SDKCollect the payment method beautifully
Payment intentYour server, with the secret keyDecide amount, currency, and customer
Webhook handlerYour serverLearn the true outcome and fulfill
Publishable keyThe app, safelyIdentify your account; it is public by design
Secret keyServer only, alwaysEverything privileged; never in the bundle

The table is also the security model. The app holds only the publishable key; the secret key lives exclusively on the server, and any generated code that puts it in the app, in a constants file, an env shipped to the client, anywhere the bundle can see, is broken regardless of whether it works. The checkout screen around the sheet, the cart, the summary, the success state, is ordinary design work, and a free VP0 checkout design gives an agent like Claude Code or Cursor a real screen to extend from its machine-readable source page while you wire the intent flow, with the visual side of that surface explored in the Stripe checkout UI guide.

Prompting an agent through the integration

Agents integrate Stripe well when the prompt carries the architecture and badly when it carries only the wish. The difference is a handful of sentences: this is an Expo project using a development build; the server (name the stack) exposes an endpoint that creates a payment intent and returns its client secret; the app calls that endpoint, presents the payment sheet with the result, and treats the sheet’s success as submitted, not paid; fulfillment happens only from the webhook. With that scaffold, the generated code lands in the right shape on the first pass.

Keep the agent off Stripe’s deprecated surfaces too. The training data is full of older patterns, tokens created client-side, charges instead of intents, card forms hand-rolled instead of the sheet, and an unpinned prompt will resurrect them confidently. Naming the current shape, payment intents plus the payment sheet, in the prompt is what anchors the generation to the API that actually ships today.

Two more prompt lines pay for themselves. Pin the test flow: use Stripe’s test keys and test cards, including the decline and authentication-required cards, not just the happy 4242 card, because generated integrations are reliably tested against success only. And demand idempotency on the server endpoint, so a retried request cannot create two intents for one cart, the same exactly-once discipline that any money-moving surface needs, from Apple Pay buttons to approval gates.

Apple’s rules: when Stripe is allowed at all

Stripe in an iOS app is for physical goods and real-world services, the food delivery, the booking, the marketplace order; digital content and features consumed in the app must use Apple’s in-app purchase system under App Review Guideline 3.1.1. This is the part of a payments integration no SDK can fix, and AI builders will happily generate a Stripe paywall for premium app features that sails straight into rejection.

So classify the product before the prompt. Selling tangible things or services delivered outside the app: Stripe is correct and expected. Selling digital unlocks, subscriptions to in-app features, content consumed on the device: that is IAP territory, and the architecture is different from the first file. Mixed products carry both rails honestly, with each purchase type on its lawful one. The classification takes five minutes and determines the entire integration, which is why it belongs before the code, not in the rejection appeal.

Common mistakes when vibe coding Stripe in Expo

The defining one is optimistic fulfillment: the generated app marks the order paid when the payment sheet returns success, and no webhook exists at all. The sheet’s success means the payment was submitted; the webhook’s event means it settled. Products built on the first signal eventually ship goods for payments that failed asynchronously, and the bug surfaces as accounting, months later, which is the most expensive place a software bug can possibly surface.

Three more recur. The secret key drifts into the client, usually as an environment variable the agent helpfully inlines into the bundle; audit for it explicitly, because it works in testing and is a breach in production. Amounts get computed on the client and trusted by the server, letting any tampered request set its own price; the server owns amounts, the client owns display. And the integration ships having seen only the happy-path test card, with declines, 3D Secure challenges, and network drops unrehearsed, so error states are blank exactly where users are most anxious. Each is one prompt sentence to prevent and one incident to discover.

Key takeaways: Stripe in an Expo app

  • Three pieces, three places. Sheet in the app, intents on the server, truth in the webhook.
  • Development build, not Expo Go. Native SDKs do not run in the sandbox app; say so in the prompt.
  • The secret key never ships. Publishable in the app, secret on the server, amounts decided server-side.
  • Sheet success is submitted, not paid. Fulfill from the webhook only.
  • Classify the product first. Physical and real-world: Stripe. Digital in-app: Apple’s IAP. Mixed: both rails honestly.

The practical build order

Classify the product against Apple’s rules, stand up the server endpoint and webhook with test keys, then wire the app: development build, the SDK, a checkout screen started from a free VP0 design and extended by your agent, the sheet fed by the server’s client secret. Rehearse the unhappy paths with Stripe’s decline and authentication test cards before any real key exists in any environment, and keep the secret key audit, grep the bundle, literally, as a release habit. If what you are selling turns out to be digital features, stop and switch rails to IAP before building further, because no amount of good Stripe engineering survives the category being wrong. The integration itself is a few honest days once the architecture is stated, and most of the horror stories are the architecture never having been stated.

Frequently asked questions

How do I integrate Stripe into an Expo app with AI? Give the agent the architecture, not just the wish: an Expo project on a development build, a server endpoint that creates a payment intent and returns its client secret, the app presenting the native payment sheet with that secret, and fulfillment driven only by the webhook. Add the test discipline, decline and 3D Secure cards, not just the happy one, and the idempotency requirement on the server endpoint. A free VP0 checkout design gives the agent the screen to extend while you wire the flow.

Why does Stripe not work in Expo Go? Because the Stripe SDK is a native module and Expo Go only ships with the native modules Expo bundles into it. The fix is a development build, your own variant of the Go client compiled with your project’s native dependencies, after which the normal Expo workflow continues. It is a one-time setup, not an ejection. The crash or missing payment sheet that looks like a Stripe bug is almost always this runtime fact, met mid-project because nobody told the agent up front.

Where do the Stripe keys go in an Expo app? The publishable key ships in the app; it is public by design and only identifies your account. The secret key lives exclusively on the server, used to create intents and verify webhooks, and must never appear in the bundle, a client environment variable, or a constants file. Generated code drifts on this constantly, so audit explicitly: search the built bundle for the key prefix before release. Amounts follow the same rule, computed and enforced on the server, displayed by the client.

Can I use Stripe instead of Apple in-app purchase? Only for physical goods and services consumed outside the app, deliveries, bookings, marketplace orders. Digital content and features used inside the app must go through Apple’s in-app purchase under Guideline 3.1.1, and a Stripe paywall for premium app features is a standard rejection. Classify what you sell before writing any payment code, and if the product mixes both, run both rails, each purchase type on its lawful one. The classification, not the SDK, decides the architecture.

When is the payment actually complete? When your webhook receives the success event and your server records it, and not before. The payment sheet returning success means the payment was submitted; settlement is asynchronous and can still fail. Fulfill orders, grant access, and update balances only from the webhook, keep the endpoint idempotent so replayed events cannot double-fulfill, and let the app’s success screen say what is true: order received, confirmation coming. Optimistic fulfillment is the classic generated-code bug, and it surfaces as accounting.

Other questions VP0 users ask

How do I integrate Stripe into an Expo app with AI?

Give the agent the architecture, not just the wish: an Expo project on a development build, a server endpoint that creates a payment intent and returns its client secret, the app presenting the native payment sheet with that secret, and fulfillment driven only by the webhook. Add the test discipline, decline and 3D Secure cards, not just the happy one, and the idempotency requirement on the server endpoint. A free VP0 checkout design gives the agent the screen to extend while you wire the flow.

Why does Stripe not work in Expo Go?

Because the Stripe SDK is a native module and Expo Go only ships with the native modules Expo bundles into it. The fix is a development build, your own variant of the Go client compiled with your project's native dependencies, after which the normal Expo workflow continues. It is a one-time setup, not an ejection. The crash or missing payment sheet that looks like a Stripe bug is almost always this runtime fact, met mid-project because nobody told the agent up front.

Where do the Stripe keys go in an Expo app?

The publishable key ships in the app; it is public by design and only identifies your account. The secret key lives exclusively on the server, used to create intents and verify webhooks, and must never appear in the bundle, a client environment variable, or a constants file. Generated code drifts on this constantly, so audit explicitly: search the built bundle for the key prefix before release. Amounts follow the same rule, computed and enforced on the server, displayed by the client.

Can I use Stripe instead of Apple in-app purchase?

Only for physical goods and services consumed outside the app, deliveries, bookings, marketplace orders. Digital content and features used inside the app must go through Apple's in-app purchase under Guideline 3.1.1, and a Stripe paywall for premium app features is a standard rejection. Classify what you sell before writing any payment code, and if the product mixes both, run both rails, each purchase type on its lawful one. The classification, not the SDK, decides the architecture.

When is the payment actually complete?

When your webhook receives the success event and your server records it, and not before. The payment sheet returning success means the payment was submitted; settlement is asynchronous and can still fail. Fulfill orders, grant access, and update balances only from the webhook, keep the endpoint idempotent so replayed events cannot double-fulfill, and let the app's success screen say what is true: order received, confirmation coming. Optimistic fulfillment is the classic generated-code bug, and it surfaces as accounting.

Part of the Payments, Monetization & Regional Fintech hub. Browse all VP0 topics →

Keep reading

Stripe Redirect Checkout on iOS: What's Actually Allowed: a glowing iPhone home-screen icon on a purple and blue gradient
Workflows 5 min read

Stripe Redirect Checkout on iOS: What's Actually Allowed

Thinking of a Stripe redirect checkout to skip App Store fees? Here is when external checkout is allowed (physical goods, entitlements) and when Apple requires IAP.

Lawrence Arya · June 1, 2026
iDEAL QR Scanner Payment UI for iOS: How to Build It: a reflective 3D App Store icon on a blue and purple gradient
Guides 9 min read

iDEAL QR Scanner Payment UI for iOS: How to Build It

An iDEAL QR scan routes the user to their own bank to authorize a payment. Here is the iOS scanner UI: capture, confirmation, handoff, and honest states.

Lawrence Arya · June 10, 2026
iDEAL Bank Selector UI for iOS: The Right Pattern: a vivid neon 3D App Store icon on an orange, pink and blue gradient
Guides 5 min read

iDEAL Bank Selector UI for iOS: The Right Pattern

The iDEAL bank selector is a redirect picker, not a card form. Here is how to build it on iOS, where the bank list comes from, and the iDEAL 2.0 change to watch.

Lawrence Arya · June 4, 2026
UPI and Paytm Deep Linking in SwiftUI: The Safe Pattern: a glass photo icon surrounded by chat, music, heart, camera and shopping app icons on a pastel gradient
Guides 5 min read

UPI and Paytm Deep Linking in SwiftUI: The Safe Pattern

How UPI and Paytm deep linking works in a SwiftUI app: upi:// links, canOpenURL setup, PSP status verification, and the four screens the flow needs.

Lawrence Arya · June 4, 2026
Freemium vs Free Trial: Paywall Design Compared: a glass app tile showing the VP0 logo on a pink and blue gradient
Guides 4 min read

Freemium vs Free Trial: Paywall Design Compared

Freemium and free trials lead to different paywalls and different users. A clear comparison of the two models and how to build either from a free VP0 design.

Lawrence Arya · May 31, 2026
Municipal Parking Ticket Scanner Payment App UI: a reflective 3D App Store icon on a blue and purple gradient
Guides 4 min read

Municipal Parking Ticket Scanner Payment App UI

Build a parking ticket pay-by-scan app in SwiftUI: scan the citation, see the details, and pay, from a free VP0 design. Certified payments, honest fees.

Lawrence Arya · May 31, 2026