# 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* (Account configurations represent role-based functionality that you can enable for accounts, such as merchant, customer, or recipient). It only supports `Customer` objects.

# Collect and save a payment method


A SetupIntent flow allows you to collect payment method details and save them for future payments without creating a charge. In this integration, you build a custom flow where you render the Payment Element, create the *SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method), and confirm saving the payment method 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:

#### Ruby

```bash
# Available as a gem
sudo gem install stripe
```

```ruby
# If you use bundler, you can add this line to your Gemfile
gem 'stripe'
```

### Client-side 

The [React Native SDK](https://github.com/stripe/stripe-react-native) is open source and fully documented. Internally, it uses the [native iOS](https://github.com/stripe/stripe-ios) and [Android](https://github.com/stripe/stripe-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):

#### yarn

```bash
yarn add @stripe/stripe-react-native
```

#### npm

```bash
npm install @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.

> We recommend following the [official TypeScript guide](https://reactnative.dev/docs/typescript#adding-typescript-to-an-existing-project) 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](https://docs.stripe.com/keys.md#obtain-api-keys) in `publishableKey` is required. The following example shows how to initialize Stripe using the `StripeProvider` component.

```jsx
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>
  );
}
```

> Use your API [test keys](https://docs.stripe.com/keys.md#obtain-api-keys) while you test and develop, and your [live mode](https://docs.stripe.com/keys.md#test-live-modes) keys when you publish your app.

## Enable payment methods

> When used with a SetupIntent, PaymentSheet supports cards and PayPal.

View your [payment methods settings](https://dashboard.stripe.com/settings/payment_methods) and enable the payment methods you want to support. You need at least one payment method enabled to create a *SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method).

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](https://docs.stripe.com/payments/payment-methods/payment-method-support.md) for product and payment method support, and our [pricing page](https://stripe.com/pricing/local-payment-methods) 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](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app#Register-your-URL-scheme) a custom URL. Universal links aren’t supported.
2. [Configure](https://reactnative.dev/docs/linking) your custom URL.
3. Set up your root component to forward the URL to the Stripe SDK as shown below.

> If you’re using Expo, [set your scheme](https://docs.expo.io/guides/linking/#in-a-standalone-app) in the `app.json` file.

```jsx
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:

```js
await initPaymentSheet({
  ...
  returnURL: 'your-app://stripe-redirect',
  ...
});
```

For more information on native URL schemes, refer to the [Android](https://developer.android.com/training/app-links/deep-linking) and [iOS](https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app) docs.

## Create a Customer [Server-side]

To set up a payment method for future payments, you must attach it to an object that represents your customer. When your customer creates an account or has their first transaction with your business, create either a customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/create.md) object with the Accounts v2 API or a [Customer](https://docs.stripe.com/api/customers/create.md) object with the Customers API.

> #### Use the Accounts v2 API to represent customers
> 
> The Accounts v2 API is generally available for Connect users, and in public preview for other Stripe users. If you’re part of the Accounts v2 preview, you need to specify a [preview version](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning) in your code.
> 
> To join the Accounts v2 preview, go to [Account previews and features](https://dashboard.stripe.com/settings/previews) in your Dashboard and enable **Reusable payment methods for Global Payouts**.
> 
> For most use cases, we recommend [modeling your customers as customer-configured Account objects](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md) instead of using [Customer](https://docs.stripe.com/api/customers.md) objects.

```curl
curl -X POST https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:"
```

## Collect payment details [Client-side]

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

| Default                                                                                                                                     | Custom flow                                                                                                                                                                  |
| ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ![PaymentSheet](https://b.stripecdn.com/docs-statics-srv/assets/ios-overview.9e0d68d009dc005f73a6f5df69e00458.png)                          | ![Custom flow](https://b.stripecdn.com/docs-statics-srv/assets/ios-multi-step.cd631ea4f1cd8cf3f39b6b9e1e92b6c5.png)                                                          |
| Displays a sheet to collect payment details and complete the setup. The button in the sheet says **Set up** and sets up the payment method. | 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 the setup. |

#### Default

### Initialize PaymentSheet

When you’re ready to set up a payment method, initialize the PaymentSheet with an `intentConfiguration`. The `intentConfiguration` object contains details about the specific payment method, such as the currency, and a `confirmationTokenConfirmHandler` callback.

```jsx
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: {
          currencyCode: 'USD',
          setupFutureUsage: 'OffSession',
        },
        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 setting up their payment method, and then the sheet is dismissed.

```jsx
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 {
      //Setupcompleted - show a confirmation screen.
    }
  }
  // ...
}

```

### Confirm the payment details

When the customer taps **Set up** 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 SetupIntent 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 SetupIntent using the client secret or displays the localized error message in its UI.

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

  const confirmHandler = async (
    confirmationToken,
    intentCreationCallback: (params: IntentCreationCallbackParams) => void
  ) => {
    // Make a request to your own server to create aSetupIntentand 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',
        },
      });
    }
  }
  // ...
}
```

#### Custom flow

This integration assumes that your checkout screen has two buttons: a **Payment Method** button that presents the PaymentSheet to collect payment details, and a **Save** button that saves the payment method.

### Initialize PaymentSheet

When your checkout screen loads, initialize the PaymentSheet with an `intentConfiguration`. The `intentConfiguration` object contains details about the specific payment method, such as the currency, and a `confirmationTokenConfirmHandler` callback.

First, call `initPaymentSheet` and pass `customFlow: true`. `initPaymentSheet` resolves with an initial payment option containing an image and label representing the customer’s payment method. Update your UI with these details.

```jsx
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,
    confirmPaymentSheetPayment,
  } = useStripe();

  const initializePaymentSheet = async () => {
    const { error, paymentOption } = await initPaymentSheet({
      merchantDisplayName: "Example, Inc.",
      customFlow: true,
      intentConfiguration: {
        mode: {
          currencyCode: 'USD',
          setupFutureUsage: 'OffSession',
        },
        confirmationTokenConfirmHandler: confirmHandler
      }
    });
    if (error) {
      // handle error
    }
    // Update your UI with paymentOption
  };

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

  const confirmHandler = async (
    confirmationToken: {id: string},
    intentCreationCallback: (params: IntentCreationCallbackParams) => void
  ) => {
    // You'll implement this in the "Confirm the payment" section below
  }
  const didTapCheckoutButton = async () => {
    // You'll implement this in the "Confirm the payment" section below
  }
  return (
    <View>
      <Button
        title="Checkout"
        onPress={didTapCheckoutButton}
      />
    </View>
  );
}
```

### Present PaymentSheet

When a customer taps **Payment Method**, call `presentPaymentSheet` to collect payment details. When this completes, update your UI again with the `paymentOption` property.

```javascript
const { error, paymentOption } = await presentPaymentSheet();
// Update your UI with paymentOption
```

### Confirm the payment details

When the customer taps **Save**, call `confirmPaymentSheetPayment`. It calls the `confirmationTokenConfirmHandler` 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 SetupIntent 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 SetupIntent using the client secret or displays the localized error message in its UI.

```jsx
export default function CheckoutScreen() {
  // ...
  const didTapCheckoutButton = async () => {
    const { error } = await confirmPaymentSheetPayment();

    if (error) {
      // PaymentSheet encountered an unrecoverable error. You can display the error to the user, log it, and so on.
    } else {
      //Setupcompleted - show a confirmation screen.
    }
  }

  const confirmHandler = async (
    confirmationToken: {id: string},
    intentCreationCallback: (params: IntentCreationCallbackParams) => void
  ) => {
    // Make a request to your own server to create aSetupIntentand 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 the client secret or error.
    const { client_secret, error } = await response.json();
    if (client_secret) {
      intentCreationCallback({clientSecret: client_secret});
    } else {
      intentCreationCallback({
        error: {
          code: 'Failed',
          message: error?.message || 'An error occurred',
          localizedMessage: error?.localizedMessage || 'An error occurred',
        },
      });
    }
  }
  // ...
}

```

The server code is explained in the following step.

## Create a SetupIntent [Server-side]

On your server, create a *SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method). You can manage payment methods from the [Dashboard](https://dashboard.stripe.com/settings/payment_methods). Stripe evaluates payment method restrictions and other parameters to determine the list of supported payment methods.

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

If the call succeeds, return the SetupIntent *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)). If the call fails, [handle the error](https://docs.stripe.com/error-handling.md) and return an error message with a brief explanation for your customer.

> Verify that all IntentConfiguration properties match your SetupIntent (for example, [usage](https://docs.stripe.com/api/setup_intents/object.md#setup_intent_object-usage)).

#### Ruby

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

post '/create-intent' do
  data = JSON.parse request.body.read
  params = {
    customer: ..., # The Customer ID you previously created
    automatic_payment_methods: {enabled: true},
  }
  begin
    intent = client.v1.setup_intents.create(params)
    {client_secret: intent.client_secret}.to_json
  rescue Stripe::StripeError => e
    {error: e.error.message}.to_json
  end
end
```

## Charge the saved payment method later [Server-side]

> When you save a `bancontact` or `ideal` payment method, it generates and saves a `sepa_debit` reusable payment method instead of the original method. To query for the saved method, you need to use `sepa_debit`, not `bancontact` or `ideal`.

To find a payment method to charge, list the payment methods associated with your customer. This example lists cards, but you can list any supported [type](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-type).

> #### Use the Accounts v2 API to represent customers
> 
> The Accounts v2 API is generally available for Connect users, and in public preview for other Stripe users. If you’re part of the Accounts v2 preview, you need to specify a [preview version](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning) in your code.
> 
> To join the Accounts v2 preview, go to [Account previews and features](https://dashboard.stripe.com/settings/previews) in your Dashboard and enable **Reusable payment methods for Global Payouts**.
> 
> For most use cases, we recommend [modeling your customers as customer-configured Account objects](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md) instead of using [Customer](https://docs.stripe.com/api/customers.md) objects.

#### Accounts v2

```curl
curl -G https://api.stripe.com/v1/payment_methods \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d type=card
```

#### Customers v1

```curl
curl -G https://api.stripe.com/v1/payment_methods \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d type=card
```

When you’re ready to charge your customer *off-session* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information), use the ID of the `Customer` or customer-configured `Account` and the `PaymentMethod` ID to create a `PaymentIntent` with the amount and currency of the payment. Set a few other parameters to make the off-session payment:

- Set [off_session](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-off_session) to true to indicate that the customer isn’t in your checkout flow to respond to any authentication requests. If, during your checkout flow, a partner (such as a card issuer or bank) requests authentication, Stripe requests exemptions using customer information from a previous *on-session* (A payment is described as on-session if it occurs while the customer is actively in your checkout flow and able to authenticate the payment method) transaction. If the conditions for exemption aren’t met, the `PaymentIntent` might throw an error.
- Set [confirm](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-confirm) to true to trigger confirmation immediately when the `PaymentIntent` is created.
- Set [payment_method](https://docs.stripe.com/api.md#create_payment_intent-payment_method) to the `PaymentMethod`’s ID.
- Depending on how you represent customers in your integration, set either [customer_account](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer_account) to the ID of the customer-configured `Account` or [customer](https://docs.stripe.com/api.md#create_payment_intent-customer) to the ID of the `Customer`.

#### Accounts v2

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d "automatic_payment_methods[enabled]=true" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d payment_method={{PAYMENT_METHOD_ID}} \
  --data-urlencode "return_url=https://example.com/order/123/complete" \
  -d off_session=true \
  -d confirm=true
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d "automatic_payment_methods[enabled]=true" \
  -d "customer={{CUSTOMER_ID}}" \
  -d payment_method={{PAYMENT_METHOD_ID}} \
  --data-urlencode "return_url=https://example.com/order/123/complete" \
  -d off_session=true \
  -d confirm=true
```

## Test the integration

#### Cards

| Card number         | Scenario                                                                                                                                                                                                                                                                                      | How to test                                                                                           |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| 4242424242424242    | 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. |
| 4000002500003155    | The card payment requires *authentication* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase). | Fill out the credit card form using the credit card number with any expiration, CVC, and postal code. |
| 4000000000009995    | 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. |
| 6205500000000000004 | 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. |

#### Bank redirects

| Payment method    | Scenario                                                                                                                                                                                        | How to test                                                                                                                                              |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bancontact, iDEAL | Your customer fails to authenticate on the redirect page for a redirect-based and immediate notification payment method.                                                                        | Choose any redirect-based payment method, fill out the required details, and confirm the payment. Then click **Fail test payment** on the redirect page. |
| Pay by Bank       | Your customer successfully pays with a redirect-based and [delayed notification](https://docs.stripe.com/payments/payment-methods.md#payment-notification) payment method.                      | Choose the payment method, fill out the required details, and confirm the payment. Then click **Complete test payment** on the redirect page.            |
| Pay by Bank       | Your customer fails to authenticate on the redirect page for a redirect-based and delayed notification payment method.                                                                          | Choose the payment method, fill out the required details, and confirm the payment. Then click **Fail test payment** on the redirect page.                |
| BLIK              | BLIK payments fail in a variety of ways—immediate failures (for example, the code is expired or invalid), delayed errors (the bank declines) or timeouts (the customer didn’t respond in time). | Use email patterns to [simulate the different failures.](https://docs.stripe.com/payments/blik/accept-a-payment.md#simulate-failures)                    |

#### Bank debits

| Payment method    | Scenario                                                                                          | How to test                                                                                                                                                                                       |
| ----------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SEPA Direct Debit | Your customer successfully pays with SEPA Direct Debit.                                           | Fill out the form using the account number `AT321904300235473204`. The confirmed PaymentIntent initially transitions to processing, then transitions to the succeeded status three minutes later. |
| SEPA Direct Debit | Your customer’s payment intent status transitions from `processing` to `requires_payment_method`. | Fill out the form using the account number `AT861904300235473202`.                                                                                                                                |

See [Testing](https://docs.stripe.com/testing.md) for additional information to test your integration.

## Enable card scanning

> 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](https://developers.google.com/pay/api/android/guides/test-and-deploy/request-prod-access) to the Google Pay API from the [Google Pay and Wallet Console](https://pay.google.com/business/console?utm_source=devsite&utm_medium=devsite&utm_campaign=devsite).

- 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](https://developers.google.com/android/reference/com/google/android/gms/wallet/PaymentsClient#getPaymentCardRecognitionIntent%28com.google.android.gms.wallet.PaymentCardRecognitionIntentRequest%29)
- **Important:** The card scanning feature only appears in builds signed with the same signing key registered in the [Google Pay & Wallet Console](https://pay.google.com/business/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.

> The Stripe card scanner is in public preview.

To enable card scanning support, add `stripecardscan` to the `dependencies` block of your [app/build.gradle](https://developer.android.com/studio/build/dependencies) file:

#### Groovy

```groovy
implementation 'com.stripe:stripecardscan:23.22.0'
```

## Optional: Enable saved cards [Server-side] [Client-side]

`PaymentSheet` can allow the customer to save their card and can include the customer’s saved cards in available payment methods. The customer must have an associated customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) object or a [Customer](https://docs.stripe.com/api/customers/create.md) object on your server. To enable a checkbox that allows the customer to save their card, create a [CustomerSession](https://docs.stripe.com/api/customer_sessions.md) with `payment_method_save` set to `enabled`.

Set the same `Account` or `Customer` on the underlying `PaymentIntent` or `SetupIntent` that you set on the `CustomerSession`.

#### Accounts v2

```javascript
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
const stripe = require("stripe")("<<YOUR_SECRET_KEY>>");
const express = require('express');
const app = express();

app.set('trust proxy', true);
app.use(express.json());

app.post('/payment-sheet', async (req, res) => {
  // Use an existing Account ID if this is a returning customer.
  const customer_account = await stripe.v2.core.accounts.create();
  const customerSession = await stripe.customerSessions.create({
    customer_account: customer_account.id,
      mobile_payment_element: {
        enabled: true,
        features: {
          payment_method_save: 'enabled',
          payment_method_redisplay: 'enabled',
          payment_method_remove: 'enabled'
        }
      },
  });

  res.json({
    customerSessionClientSecret: customerSession.client_secret,
    customer_account: customer_account.id,
  });
});
```

Next, configure PaymentSheet with the customer’s ID and the CustomerSession client secret.

```jsx
const { error } = await initPaymentSheet({
  merchantDisplayName: "Example, Inc.",
  customerId: customer_account,
  customerSessionClientSecret: customerSessionClientSecret,
  ...
});
```

#### Customers v1

```javascript
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
const stripe = require("stripe")("<<YOUR_SECRET_KEY>>");
const express = require('express');
const app = express();

app.set('trust proxy', true);
app.use(express.json());

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 customerSession = await stripe.customerSessions.create({
    customer: customer.id,
      mobile_payment_element: {
        enabled: true,
        features: {
          payment_method_save: 'enabled',
          payment_method_redisplay: 'enabled',
          payment_method_remove: 'enabled'
        }
      },
  });

  res.json({
    customerSessionClientSecret: customerSession.client_secret,
    customer: customer.id,
  });
});
```

Next, configure PaymentSheet with the Customer’s ID and the CustomerSession client secret.

```jsx
const { error } = await initPaymentSheet({
  merchantDisplayName: "Example, Inc.",
  customerId: customer,
  customerSessionClientSecret: customerSessionClientSecret,
  ...
});
```

## Optional: Allow delayed payment methods [Client-side]

*Delayed payment methods* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these 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.

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

```jsx
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 `.completed`.

## Optional: Enable Apple Pay

### Register for an Apple Merchant ID

Obtain an Apple Merchant ID by [registering for a new identifier](https://developer.apple.com/account/resources/identifiers/add/merchant) 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.com.{{YOUR_APP_NAME}}`).

### Create a new Apple Pay certificate

Create a certificate for your app to encrypt payment data.

Go to the [iOS Certificate Settings](https://dashboard.stripe.com/settings/ios_certificates) 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](https://dashboard.stripe.com/settings/ios_certificates) 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.
![](https://b.stripecdn.com/docs-statics-srv/assets/xcode.a701d4c1922d19985e9c614a6f105bf1.png)

Enable the Apple Pay capability in Xcode

### Add Apple Pay

#### One-time payment

Pass your merchant ID when you create `StripeProvider`:

```javascript
import { StripeProvider } from '@stripe/stripe-react-native';

function App() {
  return (
    <StripeProvider
      publishableKey="<<YOUR_PUBLISHABLE_KEY>>"
      merchantIdentifier="MERCHANT_ID"
    >
      {/* Your app code here */}
    </StripeProvider>
  );
}
```

When you call `initPaymentSheet`, pass in your [ApplePayParams](https://stripe.dev/stripe-react-native/api-reference/modules/PaymentSheet.html#ApplePayParams):

```javascript
await initPaymentSheet({
  // ...
  applePay: {
    merchantCountryCode: 'US',
  },
});
```

#### Recurring payments

When you call `initPaymentSheet`, pass in an [ApplePayParams](https://stripe.dev/stripe-react-native/api-reference/modules/PaymentSheet.html#ApplePayParams) with `merchantCountryCode` set to the country code of your business.

In accordance with [Apple’s guidelines](https://developer.apple.com/design/human-interface-guidelines/apple-pay#Supporting-subscriptions) for recurring payments, you must also set a `cardItems` that includes a [RecurringCartSummaryItem](https://stripe.dev/stripe-react-native/api-reference/modules/ApplePay.html#RecurringCartSummaryItem) with the amount you intend to charge (for example, “59.95 USD a month”).

You can also adopt [merchant tokens](https://developer.apple.com/apple-pay/merchant-tokens/) by setting the `request` with its `type` set to `PaymentRequestType.Recurring`

To learn more about how to use recurring payments with Apple Pay, see [Apple’s PassKit documentation](https://developer.apple.com/documentation/passkit/pkpaymentrequest).

#### iOS (React Native)

```javascript
const initializePaymentSheet = async () => {
  const recurringSummaryItem = {
    label: 'My Subscription',
    amount: '59.99',
    paymentType: 'Recurring',
    intervalCount: 1,
    intervalUnit: 'month',
    // Payment starts today
    startDate: new Date().getTime() / 1000,

    // Payment ends in one year
    endDate: new Date().getTime() / 1000 + 60 * 60 * 24 * 365,
  };

  const {error} = await initPaymentSheet({
    // ...
    applePay: {
      merchantCountryCode: 'US',
      cartItems: [recurringSummaryItem],
      request: {
        type: PaymentRequestType.Recurring,
        description: 'Recurring',
        managementUrl: 'https://my-backend.example.com/customer-portal',
        billing: recurringSummaryItem,
        billingAgreement:
          "You'll be billed $59.99 every month for the next 12 months. To cancel at any time, go to Account and click 'Cancel Membership.'",
      },
    },
  });
};
```

### Order tracking

To add [order tracking](https://developer.apple.com/design/human-interface-guidelines/technologies/wallet/designing-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](https://developer.apple.com/documentation/walletorders).

#### iOS (React Native)

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

## Optional: Enable 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**:

```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](https://developers.google.com/pay/api/android/guides/setup) 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](https://reactnative.dev/docs/running-on-device) to test your application on a physical device.

```javascript
const { error, paymentOption } = await initPaymentSheet({
  // ...
  googlePay: {
    merchantCountryCode: 'US',
    testEnv: true, // use test environment
  },
});
```

## Optional: Customize 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](https://docs.stripe.com/elements/appearance-api/mobile.md?platform=react-native).

### Merchant display name

Specify a customer-facing business name by setting `merchantDisplayName`. By default, this is your app’s name.

```javascript
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.

```javascript
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)
```

