Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Finance automation
Platforms and marketplaces
Money management
Developer tools
Get started
Payments
Finance automation
Get started
Payments
Finance automation
Platforms and marketplaces
Money management
Overview
About Stripe payments
Upgrade your integration
Payments analytics
Online payments
OverviewFind your use caseManaged Payments
Use Payment Links
Build a checkout page
Build an advanced integration
Build an in-app integration
    Overview
    Payment Sheet
    Embedded Payment Element
      Accept in-app payments
      Customize look and feel
      Add custom payment methods
      Filter card brands
    Link out for in-app purchases
    Collect addresses
    US and Canadian cards
Payment methods
Add payment methods
Manage payment methods
Faster checkout with Link
Payment interfaces
Payment Links
Checkout
Web Elements
In-app Elements
Payment scenarios
Custom payment flows
Flexible acquiring
Orchestration
In-person payments
Terminal
Other Stripe products
Financial Connections
Crypto
Climate
HomePaymentsBuild an in-app integrationEmbedded Payment Element

Accept in-app payments

Build a customized payments integration in your iOS, Android, or React Native app using the Embedded Payment Element.

Copy page

The Embedded Payment Element is a customizable component that renders a list of payment methods that you can add into any screen in your app. When customers interact with payment methods in the list, the component opens individual bottom sheets to collect payment details.

Public preview

Embedded Payment Element is in public preview on React Native.

The Embedded 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 Embedded Payment Element, create the PaymentIntent, and confirm the payment in your app.

Set up Stripe
Server-side
Client-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:

Command Line
Ruby
# Available as a gem sudo gem install stripe
Gemfile
Ruby
# If you use bundler, you can add this line to your Gemfile gem 'stripe'

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):

Command Line
yarn add @stripe/stripe-react-native

Next, install some other necessary dependencies:

  • For iOS, navigate 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.

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 { 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> ); }

Note

Use your API test keys while you test and develop, and your live mode keys when you publish your app.

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.

Collect payment details
Client-side

Initialize Embedded Payment Element

Use the useEmbeddedPaymentElement hook to create an instance of the Embedded Payment Element. This hook requires two configuration objects: an EmbeddedPaymentElementConfiguration and an IntentConfiguration.

The EmbeddedPaymentElementConfiguration object contains general-purpose settings for the Embedded Payment Element that typically remain constant across different payments, such as the returnURL. The IntentConfiguration object specifies details about the particular payment, including the amount and currency. It also includes a confirmHandler callback. For now, leave confirmHandler as an empty function.

After calling the hook with these configurations, you receive an object containing the embeddedPaymentElementView and other useful properties and methods. The embeddedPaymentElementView is a React component that you can directly include in your JSX to render the Embedded Payment Element.

The hook handles the initialization process internally, and you can start using the returned properties and methods immediately in your component.

import React, { useState, useCallback, useEffect } from 'react'; import { useEmbeddedPaymentElement, IntentConfiguration, EmbeddedPaymentElementConfiguration, PaymentMethod, IntentCreationCallbackParams, } from '@stripe/stripe-react-native'; function MyCheckoutComponent() { const [intentConfig, setIntentConfig] = useState<IntentConfiguration | null>(null); const [elementConfig, setElementConfig] = useState<EmbeddedPaymentElementConfiguration | null>(null); const initialize = useCallback(() => { const newIntentConfig: IntentConfiguration = { mode: { amount: 1099, currencyCode: 'USD', }, paymentMethodTypes: ['card'], // Change paymentMethodTypes to customize the payment methods you want to accept confirmHandler: async ( paymentMethod: PaymentMethod.Result, shouldSavePaymentMethod: boolean, callback: (params: IntentCreationCallbackParams) => void ) => { // ...explained later }, }; const newElementConfig: EmbeddedPaymentElementConfiguration = { merchantDisplayName: 'Your Business Name', returnURL: 'your-app://stripe-redirect', }; setIntentConfig(newIntentConfig); setElementConfig(newElementConfig); }, []); const { embeddedPaymentElementView, paymentOption, confirm, update, clearPaymentOption, loadingError, } = useEmbeddedPaymentElement( intentConfig!, elementConfig! ); useEffect(() => { initialize(); }, [initialize]); }

Add Embedded Payment Element view

After the useEmbeddedPaymentElement hook has successfully initialized, include the embeddedPaymentElementView in your component to display the Embedded Payment Element in your checkout UI.

function MyCheckoutComponent() { // Other component code remains the same return ( <View> // Handle loading errors through the loadingError property {loadingError && ( <View> <Text> Failed to load payment form: </Text> <Text> {loadingError.message} </Text> </View> )} /* Add the embedded view to your view */ {embeddedPaymentElementView} {!embeddedPaymentElementView && !loadingError && <Text>Loading embedded payment element...</Text>} </View> ); }

Now you can run your app and see the Embedded Mobile Payment Element.

(Optional) Display the selected payment option

The useEmbeddedPaymentElement hook provides a paymentOption property in its return object. You can use this to access details about the customer’s selected payment option, such as a label (for example, “····4242”), image (for example, a VISA logo), or billing details to display in your UI.

The paymentOption property is reactive, meaning it automatically updates when the selected payment option changes. You don’t need to implement a separate delegate method. Instead, you can use this property directly in your component, and React re-renders the component whenever the paymentOption changes.

import React from 'react'; import { View, Text } from 'react-native'; function MyCheckoutComponent() { // Other component code remains the same return ( // Other component code remains the same // Display the currently selected payment option <View> <Text> Selected: {paymentOption?.label ?? 'None'} </Text> </View> ); }

(Optional) Update payment details

As the customer performs actions that change the payment details (for example, applying a discount code), update the EmbeddedPaymentElement instance to reflect the new values by calling the update method. Some payment methods, like Apple Pay and Google Pay, show the amount in the UI, so make sure it’s always accurate and up to date.

When the update call completes, update your UI. The update call might change the customer’s currently selected payment option.

const handleUpdate = useCallback(async () => { // Create a new IntentConfiguration object with updated values const updatedIntentConfig: IntentConfiguration = { ...intentConfig!, mode: { amount: 1099, currencyCode: 'USD', }, paymentMethodTypes: ['card'], // Change paymentMethodTypes to customize the payment methods you want to accept }; try { const result = await update(updatedIntentConfig); switch (result.status) { case 'canceled': // Do nothing; this happens when a subsequent `update` call cancels this one break; case 'failed': // Display error to user in an alert, let users retry Alert.alert('Update Failed', result.error.message); break; case 'completed': // Update was successful // You might want to update your UI or state here break; } } catch (error) { // Handle any unexpected errors console.error('Unexpected error during update:', error); } }, [intentConfig, update]);

Confirm the payment

When the customer taps the checkout button, call the confirm() method provided by the useEmbeddedPaymentElement hook to complete the payment. Make sure to disable user interaction during the confirmation process to prevent multiple submissions or interfering actions.

import React, { useCallback, useState } from 'react'; import { View, Button, Alert, ActivityIndicator } from 'react-native'; function MyCheckoutComponent() { // Other component code remains the same const [isProcessing, setIsProcessing] = useState(false); const { confirm } = useEmbeddedPaymentElement(intentConfig!, elementConfig!); const handleConfirm = useCallback(async () => { setIsProcessing(true); // Disable user interaction, show a spinner try { const result = await confirm(); switch (result.status) { case 'completed': // Payment completed - show a confirmation screen. Alert.alert('Success', 'Payment was completed successfully!'); break; case 'failed': // Encountered an unrecoverable error. You can display the error to the user, log it, etc. Alert.alert('Error', `Payment failed: ${result.error.message}`); break; case 'canceled': // Customer canceled - you should probably do nothing. console.log('Payment was canceled by the user'); break; } } catch (error) { // Handle any unexpected errors console.error('Unexpected error during confirmation:', error); Alert.alert('Error', 'An unexpected error occurred'); } finally { setIsProcessing(false); // Re-enable user interaction, hide spinner } }, [confirm]); // The rest of the component code return ( <View> // Other UI elements <Button title="Confirm Payment" onPress={handleConfirm} disabled={isProcessing || !paymentOption} /> {isProcessing && <ActivityIndicator size="large" />} </View> ); }

Next, implement the confirmHandler callback you passed to the IntentConfiguration earlier to send a request to your server. Your server should create a PaymentIntent and return its client secret. For more information about this process, see Creating a PaymentIntent.

When the server request returns, call the intentCreationCallback with either your server response’s client secret or an error. The Embedded Payment Element will then confirm the PaymentIntent using the client secret or display a localized error message in its UI. After confirmation completes, the Embedded Payment Element becomes unusable. At this point, you should navigate the user to a receipt screen or similar confirmation page in your app.

function MyCheckoutComponent() { const handleConfirm = useCallback(async ( paymentMethod, shouldSavePaymentMethod, intentCreationCallback ) => { try { // Make a request to your own server and receive a client secret or an error. const response = await fetch('https://your-server.com/create-intent', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ paymentMethodId: paymentMethod.id, shouldSave: shouldSavePaymentMethod, // Add any other necessary data }), }); if (!response.ok) { throw new Error('Server response was not ok'); } const { clientSecret } = await response.json(); // Call the `intentCreationCallback` with the client secret intentCreationCallback({ clientSecret }); } catch (error) { // Call the `intentCreationCallback` with the error intentCreationCallback({ error }); } }, []); const intentConfig: IntentConfiguration = { mode: { amount: 1099, currencyCode: 'USD', }, confirmHandler: handleConfirm, }; // The rest of your component code return ( // Your component ); }

(Optional) Clear the selected payment option

If you have payment options external to the Embedded Payment Element, you might need to clear the selected payment option. To do this, use the clearPaymentOption function provided by the useEmbeddedPaymentElement hook to deselect the currently selected payment option.

function MyCheckoutComponent() { // The rest of your component code const handleDeselectPaymentMethod = useCallback(() => { clearPaymentOption(); }, [clearPaymentOption]); // The rest of your component code }

(Optional) Let the customer pay immediately in the sheet

To configure the button in the form sheet to immediately confirm payment, set the formSheetAction property in your EmbeddedPaymentElementConfiguration object. Set its type to ‘confirm’ and provide an onFormSheetConfirmComplete callback.

The onFormSheetConfirmComplete callback executes with the result of the payment after the sheet is dismissed. Since the embedded UI isn’t usable after payment completes, we recommend that your implementation navigates the user to a different screen, such as a receipt screen, based on the result provided in this callback.

const newElementConfig: EmbeddedPaymentElementConfiguration = { merchantDisplayName: 'Your Business Name', returnURL: 'your-app://stripe-redirect', formSheetAction: { type: 'confirm', onFormSheetConfirmComplete: (result) => { switch (result.status) { case 'completed': // Payment completed. You can show a confirmation screen. console.log("Completed"); Alert.alert("Success", "Payment completed successfully!"); break; case 'failed': // Encountered an unrecoverable error. You can display the error to the user, log it, etc. console.error(result.error); Alert.alert("Error", `Payment failed: ${result.error.message}`); break; case 'canceled': // Customer canceled - you should probably do nothing. console.log("Canceled"); break; } }, }, };

Set up a return URL (iOS Only)
Client-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 afterward. Many payment method types require a return URL, so if you fail to provide it, we can’t present those payment methods to your user, even if you’ve enabled them.

To provide a return URL:

  1. Register a custom URL. Universal links aren’t supported.
  2. Configure your custom URL.
  3. 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.json file.

App.tsx
import React, { 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> ); }

For more information on native URL schemes, refer to the Android and iOS docs.

Create a PaymentIntent
Server-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_future_usage, amount, and currency).

main.rb
Ruby
require 'stripe' Stripe.api_key =
'sk_test_BQokikJOvBiI2HlWgH4olfQ2'
post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', # In the latest version of the API, specifying the `automatic_payment_methods` parameter is optional because Stripe enables its functionality by default. automatic_payment_methods: {enabled: true}, } begin intent = Stripe::PaymentIntent.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end

Handle post-payment events
Server-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_intent.succeeded event, we recommend handling these other events when collecting payments with the Payment Element:

EventDescriptionAction
payment_intent.succeededSent when a customer successfully completes a payment.Send the customer an order confirmation and fulfill their order.
payment_intent.processingSent 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_intent.succeeded or payment_intent.payment_failed 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_failedSent when a customer attempts a payment, but the payment fails.If a payment transitions from processing to payment_failed, offer the customer another attempt to pay.

Test the integration

Card numberScenarioHow to test
The card payment succeeds and doesn’t require authentication.Fill out the credit card form using the credit card number with any expiration, CVC, and postal code.
The card payment requires authentication.Fill out the credit card form using the credit card number with any expiration, CVC, and postal code.
The card is declined with a decline code like insufficient_funds.Fill out the credit card form using the credit card number with any expiration, CVC, and postal code.
The UnionPay card has a variable length of 13-19 digits.Fill out the credit card form using the credit card number with any expiration, CVC, and postal code.

See Testing for additional information to test your integration.

OptionalEnable saved cards
Server-side
Client-side

OptionalAllow delayed payment methods
Client-side

OptionalEnable Apple Pay

OptionalEnable Google Pay

OptionalEnable card scanning

OptionalCustomize the sheet

Was this page helpful?
YesNo
Need help? Contact Support.
Join our early access program.
Check out our changelog.
Questions? Contact Sales.
LLM? Read llms.txt.
Powered by Markdoc