Stripe | Financial Infrastructure to Grow Your Revenue

Stripe | Financial Infrastructure to Grow Your Revenue

4466 articles

Accept in-app payments


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.

Accounts v2 API support

The Payment Sheet doesn’t support customer-configured Accounts. It only supports Customer objects.

iOS

Android

React Native

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

Select a language

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

# Available as a gem
sudo gem install stripe

Gemfile

Select a language

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

# 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, 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>
 );
}

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.

Set up a return URL 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. 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:

  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 { useEffect, useCallback } from 'react';
import { Linking, View } 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 details Client-side

The integration can use the default payment flow or a custom flow.

DefaultCustom 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.

Initialize PaymentSheet

When you’re ready to take a payment, for example, when a customer checks out, initialize the PaymentSheet with an intentConfiguration. The intentConfiguration object contains details about the specific payment, such as the amount and currency, and a confirmationTokenConfirmHandler callback.

import {useEffect} from 'react';
import {View, Button} from 'react-native';
import {
 useStripe,
 IntentCreationCallbackParams,
} from '@stripe/stripe-react-native';

export default function CheckoutScreen() {
 const { initPaymentSheet, presentPaymentSheet } = useStripe();

 const initializePaymentSheet = async () => {
 const { error } = await initPaymentSheet({
 merchantDisplayName: "Example, Inc.",
 intentConfiguration: {
 mode: {
 amount: 1099,
 currencyCode: 'USD',
 },
 confirmationTokenConfirmHandler: confirmHandler
 }
 });
 if (error) {
 // handle error
 }
 };

 useEffect(() => {
 initializePaymentSheet();
 }, []);

 const confirmHandler = async (
 confirmationToken: {id: string},
 intentCreationCallback: (params: IntentCreationCallbackParams) => void
 ) => {
 // explained later
 }

 const didTapCheckoutButton = async () => {
 // implement later
 }
 return (
 <View>
 <Button
 title="Checkout"
 onPress={didTapCheckoutButton}
 />
 </View>
 );
}

Present PaymentSheet

Next, present the PaymentSheet. The presentPaymentSheet method resolves with a promise when the customer finishes paying, and then the sheet is dismissed.

import { PaymentSheetError } from '@stripe/stripe-react-native';
export default function CheckoutScreen() {
 // ...
 const didTapCheckoutButton = async () => {
 const { error } = await presentPaymentSheet();

 if (error) {
 if (error.code === PaymentSheetError.Canceled) {
 // Customer canceled - you should probably do nothing.
 } else {
 // PaymentSheet encountered an unrecoverable error. You can display the error to the user, log it, and so on
 }
 } else {
 // Payment completed - show a confirmation screen.
 }
 }
 // ...
}

Confirm the payment

When the customer taps Pay in the PaymentSheet, it calls the callback you passed to initPaymentSheet with a ConfirmationToken representing the customer’s payment details and preferences.

Implement this method to send a request to your server, passing confirmationToken.id. Your server creates a PaymentIntent and returns its client secret.

When the request returns, call the intentCreationCallback with your server response’s client secret or an error. The PaymentSheet confirms the PaymentIntent using the client secret or displays the localized error message in its UI.

export default function CheckoutScreen() {
 // ...

 const confirmHandler = async (
 confirmationToken,
 intentCreationCallback: (params: IntentCreationCallbackParams) => void
 ) => {
 // Make a request to your own server to create a PaymentIntent and return its client secret
 const response = await fetch(`${API_URL}/create-intent`, {
 method: 'POST',
 headers: {
 'Content-Type': 'application/json',
 },
 body: JSON.stringify({
 confirmation_token_id: confirmationToken.id,
 }),
 });
 // Call the `intentCreationCallback` with your server response's client secret or error
 const { client_secret, error } = await response.json();
 if (client_secret) {
 intentCreationCallback({clientSecret: client_secret});
 } else {
 intentCreationCallback({
 error: {
 localizedMessage: error.localizedMessage || 'An error occurred',
 },
 });
 }
 }
 // ...
}

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.

Set the same Customer or customer-configured Account on the PaymentIntent that you set on the CustomerSession.

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

Select a language

Ruby

Python

PHP

Node.js

Java

Go

.NET

No results

require 'stripe'
# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
client = Stripe::StripeClient.new('sk_test_BQokikJOvBiI2HlWgH4olfQ2')

post '/create-intent' do
 data = JSON.parse request.body.read
 params = {
 amount: 1099,
 currency: 'usd',
 automatic_payment_methods: {enabled: true},
 }
 begin
 intent = client.v1.payment_intents.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.

Enable card scanning

Note

Enabling card scanning is required for Apple’s iOS app review process. Card scanning is not required for Android’s app review process, but we recommend enabling it.

iOS

To enable card scanning support for iOS, set the NSCameraUsageDescription ( Privacy - Camera Usage Description) in the Info.plist of your application, and provide a reason for accessing the camera (for example, “To scan cards”).

Optional Android

To enable card scanning support, request production access to the Google Pay API from the Google Pay and Wallet Console.

  • If you’ve enabled Google Pay, the card scanning feature is automatically available in our UI on eligible devices. To learn more about eligible devices, see the Google Pay API constraints
  • Important: The card scanning feature only appears in builds signed with the same signing key registered in the Google Pay & Wallet Console . Test or debug builds using different signing keys (for example, builds distributed through Firebase App Tester) won’t show the Scan card option. To test card scanning in pre-release builds, you must either:
  • Sign your test builds with your production signing key
  • Add your test signing key fingerprint to the Google Pay and Wallet Console

If your app doesn’t support Google Pay, you can use the Stripe card scanner.

Public preview

The Stripe card scanner is in public preview.

To enable card scanning support, add stripecardscan to the dependencies block of your app/build.gradle file:

build.gradle

Select a language

Groovy

Kotlin

No results

implementation 'com.stripe:stripecardscan:23.20.0'

Optional Enable saved cards Server-side Client-side

Optional Allow delayed payment methods Client-side

Optional Enable Apple Pay

Optional Enable Google Pay

Optional Customize the sheet

Last verified 2026-09-24

Is this helpful?