NewHuxly MCP — Connect Claude, Cursor & Codex.Learn more
How to Add Stripe Payments to a Mobile App
Back to Blog
GuideSep 9, 202612 min read

How to Add Stripe Payments to a Mobile App

Contents

Stripe is a good fit for mobile apps that sell physical products, real-world services, bookings, tips, donations, or marketplace transactions. The app presents a payment sheet, but your backend creates the payment, calculates the final amount, verifies webhooks, and decides when an order is actually paid.

Before writing checkout code, confirm that Stripe is the correct payment route. Digital content, premium app features, and in-app subscriptions can be subject to Apple and Google billing rules. Those belong in the store-payment flow covered in our guide to in-app purchases and the RevenueCat subscription guide.

Key takeaways:

Use Stripe for eligible physical goods and real-world services, create PaymentIntents on your backend, let the mobile app use only a publishable key and client secret, fulfill orders from verified webhook events, and never trust a client-side "payment succeeded" flag.

First, choose the right payment system

The product being purchased matters more than the visual checkout screen. Store policies and regional programs change, so check the current rules before launch.

What the user buysTypical payment routeWhy
Food delivery, salon appointment, delivery fee, physical merchandiseStripe or another card processorThe purchase is a physical good or real-world service
In-app digital feature, credits, premium content, or mobile subscriptionApple In-App Purchase and Google Play Billing, subject to current regional rulesThe purchase unlocks digital value inside the app
Donation with no in-app benefitConfirm the policy and payment provider requirements for the app's category and storefrontTreatment can depend on product and region
Marketplace saleStripe Connect or another marketplace payment systemFunds, refunds, and payouts involve multiple parties

Apple's App Review Guidelines and Google's Play payments policy are the sources to check for your release. Google also states that Play Billing is for digital items, while physical goods and services use a different payment path in its Play Billing documentation.

Do not call something a "service" when the user is really buying a digital app entitlement. That shortcut can produce a store rejection and a broken billing experience.

Use a backend-led payment architecture

The mobile app should never create charges with a Stripe secret key or choose the final price. The client sends an intent such as "buy these cart items". Your server reads the real prices, creates the order, creates a PaymentIntent, and returns only the data needed to present checkout.

text
Mobile app
  -> authenticated checkout endpoint
  -> backend prices cart and creates pending order
  -> backend creates Stripe PaymentIntent
  -> app opens PaymentSheet
  -> Stripe confirms payment
  -> Stripe webhook reaches backend
  -> backend marks order paid and starts fulfillment
ComponentResponsibility
Mobile appCart UI, address collection, PaymentSheet, and clear status screens
BackendAuthentication, price calculation, inventory check, order creation, and Stripe API calls
StripePayment method collection, authentication, payment processing, refunds, and disputes
Webhook endpointVerifies Stripe events and updates your order state idempotently
DatabaseOrders, line items, payment references, fulfillment, and audit trail

Stripe's mobile payment guide uses PaymentIntents to track a payment throughout its lifecycle and supports additional customer authentication when required.

Design the order before the payment screen

Payment is one stage in an order, booking, or service workflow. Create a record that can survive an app close, a redirected authentication step, or a late webhook.

FieldPurpose
idInternal order identifier
user_idVerified customer account
statusDraft, awaiting payment, paid, cancelled, refunded, or fulfilled
amount, currencyFinal server-calculated amount
items or line-item tableProduct, quantity, price snapshot, tax, and discount data
stripe_payment_intent_idLink to the Stripe payment record
idempotency_keySafe retry key for checkout creation
fulfillment_statusSeparate from payment status

Store money as integer minor units, such as cents, rather than a floating-point number. Your server should calculate subtotal, discounts, tax, delivery, and total from trusted catalog data.

Never accept a client request that says amount: 5 and charge it directly. A modified app can change that value. The client may propose quantities or a coupon code. The backend decides the payable total.

Create the PaymentIntent on the server

When the user taps Checkout, authenticate the request, validate the cart, reserve inventory if your business needs it, create a pending order, then create or update a PaymentIntent for that order.

Use an idempotency key so an accidental double tap or a network retry does not create duplicate PaymentIntents or duplicate orders. Keep the key stable for one checkout attempt and store it with the order.

The server response can include:

text
orderId
paymentIntentClientSecret
customerId, if you use saved payment methods
ephemeralKey or equivalent customer access data, if required by your Stripe integration

The PaymentIntent client secret is designed for the client during that checkout flow. It is not a substitute for your secret API key, and it should not be logged, placed in analytics, or reused as an order authorization token.

Stripe documents the PaymentIntent lifecycle in its Payment Intents API guide. Let Stripe and the webhook determine the final payment state instead of inventing a parallel client-only state machine.

Present PaymentSheet in the mobile app

Stripe PaymentSheet gives a native checkout UI for cards and eligible payment methods. It also reduces the amount of sensitive payment UI you need to build and maintain.

For a React Native app, initialize Stripe with the publishable key and request the server-created payment configuration only after the order is ready. Stripe's React Native payment integration shows the current setup.

The mobile flow should be explicit:

StateUI behavior
Cart reviewShow items, final server price, delivery or booking details, and policies
Creating checkoutDisable repeated taps and show progress
PaymentSheet openLet Stripe handle payment-method entry and authentication
Payment confirmed in appShow "confirming your order" while the backend verifies it
PaidShow receipt or booking confirmation from your own backend
CancelledReturn to the cart without treating it as an error
FailedShow a concise message and allow another payment attempt

Do not fulfill an order simply because PaymentSheet closes without an error. The client can lose the network after confirmation, and some payments need asynchronous processing. The app should fetch the order's latest server status after checkout.

Add Apple Pay and Google Pay when eligible

Wallet payments can shorten checkout for customers who already have an eligible card on their device. They still use the same server-created PaymentIntent and webhook confirmation model.

Stripe documents Apple Pay and Google Pay support for physical goods, services, and other eligible purchases. The React Native configuration has native build requirements. For Expo, test in a development or production build where the required native payment capability is present.

Show a wallet button only when the device, payment method, product type, and store rules support it. Keep the ordinary PaymentSheet route available as the fallback.

Treat webhooks as the payment source of truth

The webhook endpoint receives Stripe events independently of the customer's phone. It is the right place to update the order, send receipts, reserve a booking, release inventory, or begin delivery.

Your webhook handler should:

  1. Verify Stripe's webhook signature before reading the event as trusted.
  2. Store the event ID and ignore duplicates.
  3. Find the order through PaymentIntent metadata or the stored Stripe ID.
  4. Apply only valid state transitions.
  5. Trigger fulfillment once, after a successful payment state.
  6. Record a safe audit event.
text
payment event arrives
  -> verify signature
  -> deduplicate event ID
  -> update payment and order records
  -> create fulfillment job only if order first became paid

Keep the payment and fulfillment states separate. A payment can be paid while shipment, booking confirmation, or provider assignment is still pending.

If your backend returns a temporary error, Stripe may retry delivery. Idempotent processing makes that safe. Do not rely on the user reopening the app to finish an order.

Handle failed payments, refunds, and disputes

Every checkout needs a clear recovery path.

SituationProduct behavior
User cancels payment sheetKeep cart and pending order according to your expiry rule
Card is declinedShow a simple retry message and allow another method
Extra authentication is neededLet Stripe present the authentication step
Payment completes after app disconnectsOrder updates through webhook and appears on refresh
Refund is issuedUpdate the order, receipt, and fulfillment workflow
Dispute arrivesFreeze or review fulfillment according to your business policy

Refunds and disputes are business workflows as well as payment events. Decide whether a refunded booking is automatically cancelled, whether a delivered physical item needs a return process, and who can approve an operator refund.

Keep support staff on your own order screen, not inside the Stripe dashboard alone. Your database should make it clear what was bought, what the customer sees, and what has already been fulfilled.

Use Stripe Connect for marketplace payouts

If customers pay one party and your platform pays a seller, provider, driver, or creator, standard single-merchant payments are not enough. Stripe Connect supports connected accounts, transfers, payouts, and platform fee models.

Plan this before launch because it affects onboarding, identity verification, refund responsibility, disputes, tax reporting, and the wording shown to customers. Stripe's Connect direct charges documentation explains one way a connected account can be charged through a platform.

Do not build marketplace payouts as a simple bank-account field plus a manual transfer script. Use a payment platform designed for the compliance and lifecycle work.

Protect keys and checkout endpoints

Only the publishable key belongs in the app. Secret keys, webhook signing secrets, Connect account controls, and refund operations stay on the backend.

Protect checkout with:

  • Verified user identity.
  • Server-side price and inventory calculation.
  • Rate limits on checkout and coupon attempts.
  • Idempotency keys for create and confirm operations.
  • Order ownership checks before reading status.
  • Signed webhook verification.
  • Minimal logging that excludes card data and client secrets.
  • Separate test and live Stripe environments.

For the supporting account flow, see how to add user authentication to a mobile app. The same basic rule applies here: the mobile app is a public client, so privileged secrets stay on the server.

Test the entire payment lifecycle

Use Stripe test mode and test payment methods, then test real production-like builds before release. Cover more than one successful card payment.

  • Checkout with card and wallet where supported.
  • User cancels the payment sheet.
  • Declined payment and retry.
  • Slow network and app backgrounding during confirmation.
  • Duplicate checkout request.
  • Webhook is delivered twice.
  • Webhook arrives after the app has closed.
  • Order status refreshes correctly on another device.
  • Refund and cancellation paths.
  • Payment succeeds but fulfillment job fails.
  • Account A cannot view Account B's order.
  • Test and live credentials cannot be mixed.

Use the mobile app testing guide as the broader release checklist.

Stripe payment launch checklist

  • The product is eligible for Stripe checkout in the target store and region.
  • Final amount comes from the backend.
  • Each checkout has an idempotency key.
  • The app uses only the publishable key and PaymentIntent client secret.
  • Webhook signature verification and deduplication are active.
  • Order, payment, and fulfillment states are separate.
  • Wallet payments have a normal fallback.
  • Refund and dispute handling are documented.
  • Logs exclude sensitive payment values.
  • Test and production environments are separate.

Build your payments app with Huxly

Huxly helps you build the mobile checkout, secure backend, order database, Stripe PaymentSheet flow, webhook handling, and payment status screens in one project. You can test checkout on real devices, add the right payment method for your product, and prepare the finished Expo, Flutter, or SwiftUI app for TestFlight and Google Play.

FAQ

Can I use Stripe for digital subscriptions inside my mobile app?

Store rules may require Apple In-App Purchase or Google Play Billing for digital features and subscriptions. Use the official store policy for your target storefront and product type. For subscriptions that unlock app access, start with the in-app purchase and RevenueCat flow.

Does the mobile app need a Stripe secret key?

No. The app uses Stripe's publishable key and receives a client secret for a server-created PaymentIntent. Your backend holds the secret key and performs privileged Stripe operations.

Why do I need a webhook if PaymentSheet says payment succeeded?

The user's app can close or lose its connection, and payment states can change asynchronously. A verified webhook lets your backend update the order and begin fulfillment independently of the device.

Can I charge a customer twice if they tap Pay twice?

You can prevent most duplicates by disabling repeated UI taps and using a stable backend idempotency key. Your order and webhook handler should also be idempotent.

When should I use Stripe Connect?

Use it when your platform receives money on behalf of sellers, providers, drivers, creators, or other connected businesses and needs controlled payouts or platform fees.

Should I mark an order paid from the client?

No. The backend should mark it paid after verified Stripe state, normally through a webhook and a safe state transition.

Conclusion

Stripe checkout is reliable when the mobile app collects payment through a server-created intent and the backend owns price calculation, order state, and fulfillment. The PaymentSheet is the customer interface. The webhook is the durable confirmation path.

Build the failure cases before launch: cancellation, retry, duplicate requests, disconnects, refunds, and late events. That work protects revenue and gives customers a much clearer checkout experience.