Accept in-app payments
Build a customized payments integration in your iOS, Android, or React Native app using the Payment Sheet.
The Payment Sheet is a customizable component that displays a list of payment methods and collects payment details in your app using a bottom sheet.
The Payment Element allows you to accept multiple payment methods using a single integration. In this integration, you build a custom payment flow where you render the Payment Element, create the PaymentIntent, and confirm the payment in your app. To confirm the payment on the server instead, see Finalize payments on the server.
Set up StripeServer-sideClient-side
Server-side 
This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:
Client-side 
The React Native SDK is open source and fully documented. Internally, it uses the native iOS and Android SDKs. To install Stripe’s React Native SDK, run one of the following commands in your project’s directory (depending on which package manager you use):
Next, install some other necessary dependencies:
- For iOS, go to the ios directory and run
pod install
to ensure that you also install the required native dependencies. - For Android, there are no more dependencies to install.
Note
We recommend following the official TypeScript guide to add TypeScript support.
Stripe initialization
To initialize Stripe in your React Native app, either wrap your payment screen with the StripeProvider
component, or use the initStripe
initialization method. Only the API publishable key in publishableKey
is required. The following example shows how to initialize Stripe using the StripeProvider
component.
import { useState, useEffect } from 'react'; import { StripeProvider } from '@stripe/stripe-react-native'; function App() { const [publishableKey, setPublishableKey] = useState(''); const fetchPublishableKey = async () => { const key = await fetchKey(); // fetch key from your server here setPublishableKey(key); }; useEffect(() => { fetchPublishableKey(); }, []); return ( <StripeProvider publishableKey={publishableKey} merchantIdentifier="merchant.identifier" // required for Apple Pay urlScheme="your-url-scheme" // required for 3D Secure and bank redirects > {/* Your app code here */} </StripeProvider> ); }
Enable payment methods
View your payment methods settings and enable the payment methods you want to support. You need at least one payment method enabled to create a PaymentIntent.
By default, Stripe enables cards and other prevalent payment methods that can help you reach more customers, but we recommend turning on additional payment methods that are relevant for your business and customers. See Payment method support for product and payment method support, and our pricing page for fees.
Set up a return URLClient-side
When a customer exits your app (for example to authenticate in Safari or their banking app), provide a way for them to automatically return to your app. Many payment method types require a return URL. If you don’t provide one, we can’t present payment methods that require a return URL to your users, even if you’ve enabled them.
To provide a return URL:
- Register a custom URL. Universal links aren’t supported.
- Configure your custom URL.
- Set up your root component to forward the URL to the Stripe SDK as shown below.
Note
If you’re using Expo, set your scheme in the app.
file.
import { useEffect, useCallback } from 'react'; import { Linking } from 'react-native'; import { useStripe } from '@stripe/stripe-react-native'; export default function MyApp() { const { handleURLCallback } = useStripe(); const handleDeepLink = useCallback( async (url: string | null) => { if (url) { const stripeHandled = await handleURLCallback(url); if (stripeHandled) { // This was a Stripe URL - you can return or add extra handling here as you see fit } else { // This was NOT a Stripe URL – handle as you normally would } } }, [handleURLCallback] ); useEffect(() => { const getUrlAsync = async () => { const initialUrl = await Linking.getInitialURL(); handleDeepLink(initialUrl); }; getUrlAsync(); const deepLinkListener = Linking.addEventListener( 'url', (event: { url: string }) => { handleDeepLink(event.url); } ); return () => deepLinkListener.remove(); }, [handleDeepLink]); return ( <View> <AwesomeAppComponent /> </View> ); }
Additionally, set the returnURL
when you call the initPaymentSheet
method:
await initPaymentSheet({ ... returnURL: 'your-app://stripe-redirect', ... });
For more information on native URL schemes, refer to the Android and iOS docs.
Collect payment detailsClient-side
The integration can use the default payment flow or a custom flow.
Default | Custom flow |
---|---|
![]() | ![]() |
Displays a sheet to collect payment details and complete the payment. The button in the sheet says Pay $X and completes the payment. | Displays a sheet to collect payment details only. The button in the sheet says Continue and returns the customer to your app, where your own button completes payment. |
Create a PaymentIntentServer-side
On your server, create a PaymentIntent with an amount and currency. You can manage payment methods from the Dashboard. Stripe handles the return of eligible payment methods based on factors such as the transaction’s amount, currency, and payment flow. To prevent malicious customers from choosing their own prices, always decide how much to charge on the server-side (a trusted environment) and not the client.
If the call succeeds, return the PaymentIntent client secret. If the call fails, handle the error and return an error message with a brief explanation for your customer.
Note
Verify that all IntentConfiguration properties match your PaymentIntent (for example, setup_
, amount
, and currency
).
Handle post-payment eventsServer-side
Stripe sends a payment_intent.succeeded event when the payment completes. Use the Dashboard webhook tool or follow the webhook guide to receive these events and run actions, such as sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.
Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes, and malicious clients could manipulate the response. Setting up your integration to listen for asynchronous events is what enables you to accept different types of payment methods with a single integration.
In addition to handling the payment_
event, we recommend handling these other events when collecting payments with the Payment Element:
Event | Description | Action |
---|---|---|
payment_intent.succeeded | Sent when a customer successfully completes a payment. | Send the customer an order confirmation and fulfill their order. |
payment_intent.processing | Sent when a customer successfully initiates a payment, but the payment has yet to complete. This event is most commonly sent when the customer initiates a bank debit. It’s followed by either a payment_ or payment_ event in the future. | Send the customer an order confirmation that indicates their payment is pending. For digital goods, you might want to fulfill the order before waiting for payment to complete. |
payment_intent.payment_failed | Sent when a customer attempts a payment, but the payment fails. | If a payment transitions from processing to payment_ , offer the customer another attempt to pay. |
Test the integration
See Testing for additional information to test your integration.
OptionalEnable saved cardsServer-sideClient-side
PaymentSheet can display a Save this card for future use checkbox that saves the customer’s card. It can also display the customer’s saved cards. To enable this checkbox, create a Customer object on your server and an associated ephemeral key.
const stripe = require('stripe')('sk_test_your_secret_key'); app.post('/payment-sheet', async (req, res) => { // Use an existing Customer ID if this is a returning customer. const customer = await stripe.customers.create(); const ephemeralKey = await stripe.ephemeralKeys.create( {customer: customer.id}, {apiVersion: '2020-08-27'} ); res.json({ ephemeralKey: ephemeralKey.secret, customer: customer.id, }); });
Next, configure PaymentSheet with the Customer’s ID and the ephemeral key’s client secret.
const { error } = await initPaymentSheet({ merchantDisplayName: "Example, Inc.", customerId: customer, customerEphemeralKeySecret: ephemeralKey, ... });
OptionalAllow delayed payment methodsClient-side
Delayed payment methods don’t guarantee that you’ll receive funds from your customer at the end of checkout. That’s because they take time to settle (for example, US Bank Accounts, SEPA Debit, iDEAL, Bancontact, and Sofort) or because they require customer action to complete (for example, OXXO, Konbini, and Boleto).
By default, PaymentSheet doesn’t display delayed payment methods. To display them, when you call initPaymentSheet
, set allowsDelayedPaymentMethods
to true.
Note
This setting only enables the display of delayed payment methods—it doesn’t activate them. For example, OXXO isn’t supported by PaymentSheet, so allowsDelayedPaymentMethods
doesn’t allow PaymentSheet to handle OXXO payment methods. It only allows PaymentSheet to display payment methods of the delayed payment types that PaymentSheet supports. If PaymentSheet adds support for OXXO payment methods in the future, it would display them too.
const { error } = await initPaymentSheet({ merchantDisplayName: "Example, Inc.", allowsDelayedPaymentMethods: true, ... });
If the customer successfully uses one of these delayed payment methods in PaymentSheet, the payment result returned is .
.
OptionalEnable Apple Pay
Register for an Apple Merchant ID
Obtain an Apple Merchant ID by registering for a new identifier on the Apple Developer website.
Fill out the form with a description and identifier. Your description is for your own records and you can modify it in the future. Stripe recommends using the name of your app as the identifier (for example, merchant.
).
Create a new Apple Pay certificate
Create a certificate for your app to encrypt payment data.
Go to the iOS Certificate Settings in the Dashboard, click Add new application, and follow the guide.
Download a Certificate Signing Request (CSR) file to get a secure certificate from Apple that allows you to use Apple Pay.
One CSR file must be used to issue exactly one certificate. If you switch your Apple Merchant ID, you must go to the iOS Certificate Settings in the Dashboard to obtain a new CSR and certificate.
Integrate with Xcode
Add the Apple Pay capability to your app. In Xcode, open your project settings, click the Signing & Capabilities tab, and add the Apple Pay capability. You might be prompted to log in to your developer account at this point. Select the merchant ID you created earlier, and your app is ready to accept Apple Pay.

Enable the Apple Pay capability in Xcode
Add Apple Pay
Order tracking
To add order tracking information in iOS 16 or later, configure a setOrderTracking
callback function. Stripe calls your implementation after the payment is complete, but before iOS dismisses the Apple Pay sheet.
In your implementation of setOrderTracking
callback function, fetch the order details from your server for the completed order, and pass the details to the provided completion
function.
To learn more about order tracking, see Apple’s Wallet Orders documentation.
await initPaymentSheet({ // ... applePay: { // ... setOrderTracking: async complete => { const apiEndpoint = Platform.OS === 'ios' ? 'http://localhost:4242' : 'http://10.0.2.2:4567'; const response = await fetch( `${apiEndpoint}/retrieve-order?orderId=${orderId}`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }, ); if (response.status === 200) { const orderDetails = await response.json(); // orderDetails should include orderIdentifier, orderTypeIdentifier, // authenticationToken and webServiceUrl complete(orderDetails); } }, }, });
OptionalEnable Google Pay
Set up your integration
To use Google Pay, first enable the Google Pay API by adding the following to the <application>
tag of your AndroidManifest.xml:
<application> ... <meta-data android:name="com.google.android.gms.wallet.api.enabled" android:value="true" /> </application>
For more details, see Google Pay’s Set up Google Pay API for Android.
Add Google Pay
When you initialize PaymentSheet
, set merchantCountryCode
to the country code of your business and set googlePay
to true.
You can also use the test environment by passing the testEnv
parameter. You can only test Google Pay on a physical Android device. Follow the React Native docs to test your application on a physical device.
const { error, paymentOption } = await initPaymentSheet({ // ... googlePay: { merchantCountryCode: 'US', testEnv: true, // use test environment }, });
OptionalEnable card scanning
To enable card scanning support for iOS, set the NSCameraUsageDescription
(Privacy - Camera Usage Description) in the Info.
of your application, and provide a reason for accessing the camera (for example, “To scan cards”). Devices with iOS 13 or higher support card scanning.
To enable card scanning support for Android, add stripecardscan
to the dependencies
block of your app/build.gradle file:
apply plugin: 'com.android.application' android { ... } dependencies { // ... // StripeCardScan implementation 'com.stripe:stripecardscan:21.26.1' }
OptionalCustomize the sheet
All customization is configured using initPaymentSheet
.
Appearance
Customize colors, fonts, and so on to match the look and feel of your app by using the appearance API.
Merchant display name
Specify a customer-facing business name by setting merchantDisplayName
. By default, this is your app’s name.
await initPaymentSheet({ // ... merchantDisplayName: 'Example Inc.', });
Dark mode
By default, PaymentSheet
automatically adapts to the user’s system-wide appearance settings (light and dark mode). You can change this by setting the style
property to alwaysLight
or alwaysDark
mode on iOS.
await initPaymentSheet({ // ... style: 'alwaysDark', });
On Android, set light or dark mode on your app:
// force dark AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) // force light AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
Default billing details
To set default values for billing details collected in the PaymentSheet, configure the defaultBillingDetails
property. The PaymentSheet
pre-populates its fields with the values that you provide.
await initPaymentSheet({ // ... defaultBillingDetails: { email: 'foo@bar.com', address: { country: 'US', }, }, });
Collect billing details
Use billingDetailsCollectionConfiguration
to specify how you want to collect billing details in the PaymentSheet.
You can collect your customer’s name, email, phone number, and address.
If you don’t intend to collect the values that the payment method requires, you must do the following:
- Attach the values that aren’t collected by
PaymentSheet
to thedefaultBillingDetails
property. - Set
billingDetailsCollectionConfiguration.
toattachDefaultsToPaymentMethod true
.
await initPaymentSheet({ // ... defaultBillingDetails: { email: 'foo@bar.com', } billingDetailsCollectionConfiguration: { name: PaymentSheet.CollectionMode.ALWAYS, email: PaymentSheet.CollectionMode.NEVER, address: PaymentSheet.AddressCollectionMode.FULL, attachDefaultsToPaymentMethod: true }, });
Note
Consult with your legal counsel regarding laws that apply to collecting information. Only collect phone numbers if you need them for the transaction.