> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# ACH via Stripe: Drop In Guide

> Integrate ACH via Stripe on your website or mobile application with just a few lines of code.

## Before you begin

This guide assumes that you know how to

* [Show Drop-in Checkout](https://primer.io/docs/checkout/drop-in/)

## Accept payments with ACH via Stripe

### Prepare the client session

ACH via Stripe requires the following data to process a payment successfully. Pass the following data in the client session, or in the payment request (for manual payment creation).

| Parameter Name | Required | Description |
| - | - | - |
| [currencyCode](https://primer.io/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session.md#body-currency-code) | ✓ | 3-letter currency code in ISO 4217 format. For ACH via Stripe, only USD is supported. |
| [order](https://primer.io/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session.md#body-order.) [↳ lineItems](https://primer.io/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session.md#body-order-line-items) | ✓ | Details of the line items of the order |
| [metadata](https://primer.io/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session.md#body-metadata) | ✓ | In order to have ACH via Stripe successful, you need to pass your device info: IP address and user agent in the metadata.`metadata.deviceInfo.ipAddress` and `metadata.deviceInfo.userAgent` |
| [paymentMethod](https://primer.io/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session.md#request.body.paymentMethod) | ✓ | |

```javasript JSX  theme={"dark"}
"paymentMethod": {
    "vaultOnAgreement": true,
    "paymentType": "FIRST_PAYMENT"
  }
```

That should be passed if one wishes to vault ACH via Stripe.

### Prepare the SDK for payments

> [tab] Web
>
> #### Show Universal Checkout
>
> Pass device info when creating the client session
> ACH via Stripe requires that a [mandate](https://docs.stripe.com/payments/ach-debit#mandates) is accepted before the payment is completed.
>
> Mandates that are accepted online require IP address and user-agent of the device that the payment is made from.
>
> Specify this information when creating the client session, otherwise payments will remain in Pending state.
>
> ```json JSON theme={"dark"}
> {
>   ...
>   "customer": {
>     "emailAddress": "...",
>     ...
>   },
>   "metadata": {
>     "deviceInfo": {
>       "ipAddress": "127.0.0.1", // IP address of the device accepting the mandate
>       "userAgent": "Mozilla/5.0" // user-agent of the device accepting the mandate
>     }
>   },
>   ...
> }
> ```
>
> **Universal Checkout**\\
>
> ACH via Stripe is automatically presented to the customer when calling `Primer.showUniversalCheckout` with the Stripe publishable key set up.
>
> ```typescript Typescript theme={"dark"}
> const options = {
>     /* Other options... */
>     stripe: {
>         publishableKey: 'pk_...',
>     }
> }
> ```
>
> Additionally, ACH via Stripe requires to show a Mandate to the user. You can either pass the full mandate text yourself via SDK options, or pass a merchant name and rely on the mandate text Primer provides you with.
>
> ```javascript JSX theme={"dark"}
> const options = {
>     /* Other options... */
>     stripe: {
>         publishableKey: 'pk_...',
>         mandateData: {
>           merchantName?: "Your Merchant Name", 
>           fullMandateText?: "Your mandate text" // you can either pass merchantName or fullMandateText, not two at the same time.
>           // In the case of passing a fullMandateText, you need to pass it localized already. 
>         }
>     }
> }
> ```
>
> #### Customization
>
> Check the [customization guide](https://primer.io/docs/checkout/drop-in/customization.md#styling-payment-method-button) to learn how to customize payment method buttons.
>
> ```typescript Typescript theme={"dark"}
> const options = {
>   /* Other options ... */
>   style: {
>     paymentMethodButton: {
>       background: string,
>       borderRadius: number | string,
>       boxShadow: string,
>       borderColor: string,
>       height: number,
>       primaryText: TextStyle,
>       logoColor: logoColor,
>       marginTop: string,
>     },
>   },
> }
> ```

> [tab] iOS
>
> #### Pre-requisites
>
> **Pass device info when creating the client session**\\
>
> ACH via Stripe requires that a [mandate](https://docs.stripe.com/payments/ach-debit#mandates) is accepted before the payment is completed.
>
> Mandates that are accepted online require IP address and user-agent of the device that the payment is made from.
>
> Specify this information when creating the client session, otherwise payments will remain in Pending state.
>
> ```json JSON theme={"dark"}
> {
>   ...
>   "customer": {
>     "emailAddress": "...",
>     ...
>   },
>   "metadata": {
>     "deviceInfo": {
>       "ipAddress": "127.0.0.1", // IP address of the device accepting the mandate
>       "userAgent": "Mozilla/5.0" // user-agent of the device accepting the mandate
>     }
>   },
>   ...
> }
> ```
>
> **Define the Primer Stripe dependency**\\
>
> In order to be able to use ACH via Stripe you will have to install `PrimerStripeSDK` which is available via CocoaPods.
>
> ```ruby RUBY theme={"dark"}
> use_frameworks!
> target 'PrimerSDK_Example' do
>   pod 'PrimerSDK'
>   # Add the line below 👇
>   pod 'PrimerStripeSDK'
> end
> ```
>
> **Define the [Stripe publishable key](https://docs.stripe.com/keys#obtain-api-keys) and mandate information**\\
>
> It is important that you specify your Stripe publishable key along with mandate information when creating `PrimerSettings`, otherwise you'll encounter errors at app runtime.
>
> ACH via Stripe requires that a [mandate](https://docs.stripe.com/payments/ach-debit#mandates) is displayed before the payment is completed. This mandate will display automatically once a bank account is chosen.
>
> The drop-in implementation provides two ways for customizing the mandate.
>
> * you specify only your business's name and the SDK will use a predefined template:
>
> By clicking Accept, you authorize \[Business Name] to debit the selected bank account for any amount owed for charges arising from your use of \[Business Name]'s services and/or purchase of products from \[Business Name], pursuant to \[Business Name]'s website and terms, until this authorization is revoked. You may amend or cancel this authorization at any time by providing notice to \[Business Name] with 30 (thirty) days notice. If you use \[Business Name]'s services or purchase additional products periodically pursuant to \[Business Name]'s terms, you authorize \[Business Name] to debit your bank account periodically. Payments that fall outside the regular debits authorized above will only be debited after your authorization is obtained.
>
> ```swift SWIFT theme={"dark"}
> let mandateData = PrimerStripeOptions.MandateData.templateMandate(merchantName: "Business Name")
> ```
>
> * you specify the entire body of the mandate:
>
> ```swift SWIFT theme={"dark"}
> let mandateData = PrimerStripeOptions.MandateData.fullMandate(text: "... Mandate full text ...")
> ```
>
> ```swift SWIFT theme={"dark"}
> let settings = PrimerSettings(
>   // ...
>   paymentMethodOptions: PrimerPaymentMethodOptions(
>     stripeOptions: PrimerStripeOptions(
>         publishableKey: "your_stripe_publishable_key",
>         mandateData: mandateData),
>   )
>   // ...
> )
> ```
>
> Proceed with the SDK initialization:
>
> ```swift SWIFT theme={"dark"}
> Primer.shared.configure(settings: settings, delegate: self)
> ```
>
> #### Show Universal Checkout
>
> ACH via Stripe payment method is automatically presented to the customer when calling `Primer.shared.showUniversalCheckout(clientToken: self.clientToken)`.
>
> ```swift SWIFT theme={"dark"}
> class MyViewController: UIViewController, PrimerDelegate {
>     func startUniversalCheckout() {
>         Primer.shared.showUniversalCheckout(clientToken: self.clientToken)
>     }
> }
> ```
>
> #### Show Universal Checkout
>
> ACH via Stripe is automatically presented to the customer when calling `Primer.shared.showUniversalCheckout`.
>
> ```swift SWIFT theme={"dark"}
> class MyViewController: UIViewController {
>   func startUniversalCheckout() {
>     Primer.shared.showUniversalCheckout(clientToken: self.clientToken)
>   }
> }
> ```
>
> #### Customization
>
> Check the [customization guide](https://primer.io/docs/checkout/drop-in/customization.md#styling-payment-method-button) to learn how to customize payment method buttons.

> [tab] Android
>
> #### Pre-requisites
>
> Pass device info and payment type when creating the client session
> ACH via Stripe requires that a [mandate](https://docs.stripe.com/payments/ach-debit#mandates) is accepted before the payment is completed. Mandates that are accepted online require IP address and user-agent of the device that the payment is made from.
>
> Specify this information when creating the client session, otherwise payments will remain in Pending state.
>
> ```json JSON theme={"dark"}
> {
>   ...
>   customer: {
>     emailAddress: '...',
>     ...
>   },
>   "metadata": {
>     "deviceInfo": {
>       "ipAddress": "127.0.0.1", // IP address of the device accepting the mandate
>       "userAgent": "Mozilla/5.0" // user-agent of the device accepting the mandate
>     }
>   }
>   ...
> }
> ```
>
> # Define the Primer Stripe dependency
>
> Amend the dependencies section of your app's `build.gradle` to include Primer's `stripe-android` library:
>
> ```kotlin Kotlin theme={"dark"}
> dependencies {
>     implementation(platform("io.primer:bom:<version>"))
>     implementation("io.primer:android")
>     implementation("io.primer:stripe-android")
> }
> ```
>
> ```groovy Groovy theme={"dark"}
> dependencies {
>     implementation platform('io.primer:bom:<version>')
>     implementation 'io.primer:android'
>     implementation 'io.primer:stripe-android'
> }
> ```
>
> > [!NOTE]
> >
> > Replace `<version>` with your Primer SDK version. The BOM sets the library version, so leave it out. [Version alignment](https://primer.io/docs/sdk/android/v2.x.x/installation.md#version-alignment) explains how versions are set, including on older SDK versions.
>
> **Define the Stripe publishable key and mandate information**\\
>
> It is important that you specify your [Stripe publishable key](https://docs.stripe.com/keys#obtain-api-keys) along with mandate information when instantiating `PrimerSettings`, otherwise you'll encounter errors at app runtime.
>
> ACH via Stripe requires that a [mandate](https://docs.stripe.com/payments/ach-debit#mandates) is displayed before the payment is completed. This mandate will display automatically once a bank account is chosen.
>
> The drop-in implementation provides two ways for customizing the mandate.
>
> * you specify only your business's name and the SDK will use a predefined template:
>   By clicking Accept, you authorize \[Business Name] to debit the bank selected bank for any amount owed for charges arising from your use of \[Business Name]'s services and/or purchase of products from \[Business Name], pursuant to \[Business Name]'s website and terms, until this authorization is revoked. You may amend or cancel this authorization at any time by providing notice to \[Business Name] with 30 (thirty) days notice. If you use \[Business Name]'s services or purchase additional products periodically pursuant to \[Business Name]'s terms, you authorize \[Business Name] to debit your bank account periodically. Payments that fall outside the regular debits authorized above will only be debited after your authorization is obtained.
>
> ```kotlin KOTLIN theme={"dark"}
> val mandateData = PrimerStripeOptions.MandateData.TemplateMandateData("Business Name")
> ```
>
> * you specify the entire body of the mandate as a string resource:
>
> ```kotlin KOTLIN theme={"dark"}
> // 'full_mandate_data' is a custom string resource defined by your app. The naming is used as an example here, it can be replaced with any suitable identifier.
> val mandateData = PrimerStripeOptions.MandateData.FullMandateData(R.string.full_mandate_data)
> ```
>
> * you specify the entire body of the mandate as a string:
>
> ```kotlin KOTLIN theme={"dark"}
> val mandateData = PrimerStripeOptions.MandateData.FullMandateStringData("full mandate text")
> ```
>
> ```kotlin KOTLIN theme={"dark"}
> val settings = PrimerSettings(
>   // ...
>   paymentMethodOptions = PrimerPaymentMethodOptions(
>     stripeOptions = PrimerStripeOptions(
>         mandateData = mandateData,
>         publishableKey = "your_stripe_publishable_key"
>     )
>   )
>   // ...
> )
> ```
>
> Proceed with the SDK initialization:
>
> ```kotlin KOTLIN theme={"dark"}
> Primer.instance.configure(settings, listener)
> ```
>
> > [!NOTE]
> >
> > The default [onFailed](https://primer.io/docs/sdk/android/v2.x.x/primer/listeners/PrimerCheckoutListener.md#onfailed) implementation of `PrimerCheckoutListener` will emit unlocalized error messages provided by the Stripe SDK. You're welcome to override the function to suit your specific needs.
>
> For a more comprehensive understanding, refer to the [documentation](https://primer.io/docs/sdk/android/v2.x.x/primer/listeners/PrimerCheckoutListener.md) available for the `PrimerCheckoutListener` interface and to the [Initialize Universal Checkout](https://primer.io/docs/get-started/set-up-a-checkout.md) section in the Get started guide.
>
> #### Show Universal Checkout
>
> ACH via Stripe is automatically presented to the customer when calling `Primer.instance.showUniversalCheckout`.
>
> ```kotlin KOTLIN theme={"dark"}
> class CheckoutActivity : AppCompatActivity() {
>   private fun setupObservers() {
>     viewModel.clientToken.observe(this) { clientToken ->
>       showUniversalCheckout(clientToken)
>     }
>   }
>   private fun showUniversalCheckout(clientToken: String) {
>     Primer.instance.showUniversalCheckout(this, clientToken)
>   }
> }
> ```
>
> #### Show Universal Checkout
>
> ACH via Stripe is automatically presented to the customer when calling `Primer.instance.showUniversalCheckout`.
>
> ```kotlin KOTLIN theme={"dark"}
> class CheckoutActivity : AppCompatActivity() {
>   private fun setupObservers() {
>     viewModel.clientToken.observe(this) { clientToken ->
>       showUniversalCheckout(clientToken)
>     }
>   }
>   private fun showUniversalCheckout(clientToken: String) {
>     Primer.instance.showUniversalCheckout(this, clientToken)
>   }
> }
> ```
>
> #### Customization
>
> Check the [customization guide](https://primer.io/docs/checkout/drop-in/customization.md#styling-payment-method-button) to learn how to customize payment method buttons.

> [tab] React Native
>
> #### Pre-requisites
>
> **Pass device info and payment type when creating the client session**\\
>
> ACH via Stripe requires that a [mandate](https://docs.stripe.com/payments/ach-debit#mandates) is accepted before the payment is completed. Mandates that are accepted online require IP address and user-agent of the device that the payment is made from.
>
> Specify this information when creating the client session, otherwise payments will remain in Pending state.
>
> ```json JSON theme={"dark"}
> {
>   ...
>   customer: {
>     emailAddress: '...',
>     ...
>   },
>   "metadata": {
>     "deviceInfo": {
>       "ipAddress": "127.0.0.1", // IP address of the device accepting the mandate
>       "userAgent": "Mozilla/5.0" // user-agent of the device accepting the mandate
>     }
>   }
>   ...
> }
> ```
>
> Define the Primer Stripe dependency
>
> **Android**\\
>
> Amend the dependencies section of your app's `build.gradle` to include Primer's `stripe-android` library:
>
> ```groovy build.gradle theme={"dark"}
> dependencies {
>   /* Other dependencies... */
>   implementation 'io.primer:stripe-android'
> }
> ```
>
> > [!NOTE]
> >
> > Leave out the version. From React Native SDK 2.49.0, the SDK sets it for you. On earlier versions, see [Android library versions](https://primer.io/docs/sdk/react-native/v2.x.x/installation.md#android-library-versions).
>
> **iOS**\\
>
> Add the dependency to your `Podfile`:
>
> ```ruby RUBY theme={"dark"}
> /* ... other dependencies **/
> pod 'PrimerStripeSDK', 'x.y.z'
> ```
>
> Define the [Stripe publishable key](https://docs.stripe.com/keys#obtain-api-keys) and mandate information
> It is important that you specify your Stripe publishable key along with mandate information when instantiating `PrimerSettings`, otherwise you'll encounter errors at app runtime.
>
> ACH via Stripe requires that a [mandate](https://docs.stripe.com/payments/ach-debit#mandates) is displayed before the payment is completed. This mandate will display automatically once a bank account is chosen.
>
> The drop-in implementation provides two ways for customizing the mandate.
>
> * you specify only your business's name and the SDK will use a predefined template:
>   By clicking Accept, you authorize \[Business Name] to debit the selected bank account for any amount owed for charges arising from your use of \[Business Name]'s services and/or purchase of products from \[Business Name], pursuant to \[Business Name]'s website and terms, until this authorization is revoked. You may amend or cancel this authorization at any time by providing notice to \[Business Name] with 30 (thirty) days notice. If you use \[Business Name]'s services or purchase additional products periodically pursuant to \[Business Name]'s terms, you authorize \[Business Name] to debit your bank account periodically. Payments that fall outside the regular debits authorized above will only be debited after your authorization is obtained.
>
> ```javascript JS theme={"dark"}
> let settings: PrimerSettings = {
>     ...
>     paymentMethodOptions: {
>         stripeOptions: {
>             publishableKey: "your_stripe_publishable_key",
>             mandateData: {
>                 merchantName: "Business Name"
>             }
>         },
>         ...
>     }
>     ...
> }
> ```
>
> * you specify the entire body of the mandate: Use `fullMandateText` to specify the full mandate text for both iOS and Android. On Android, `fullMandateStringResourceName` will be used instead if provided.
>
> ```javascript JS theme={"dark"}
>
> // 'full_mandate_resource_key' is the name/key of a string resource defined on the Android side of your app. 
> // The naming is used as an example here, it can be replaced with any suitable identifier.
>
> let settings: PrimerSettings = {
>     ...
>     paymentMethodOptions: {
>         stripeOptions: {
>             publishableKey: "your_stripe_publishable_key",
>             mandateData: {
>                     fullMandateText: "full mandate text",
>                 fullMandateStringResourceName: "full_mandate_string_resource_name" // Android only
>             }
>         },
>         ...
>     }
>     ...
> }
> ```
>
> Proceed with the SDK initialization:
>
> ```javascript JS theme={"dark"}
> await Primer.configure(settings);
> ```
>
> > [!NOTE]
> >
> > The default [onError](https://primer.io/docs/sdk/react-native/v2.x.x/primer/methods/configure.md#parameters.settings.onError) listener will emit unlocalized error messages provided by the Stripe SDK. You're welcome to override the function to suit your specific needs.
>
> For a more comprehensive understanding, refer to the [configuration documentation](https://primer.io/docs/sdk/react-native/v2.x.x/primer/methods/configure.md) and to the [Initialize Universal Checkout](https://primer.io/docs/get-started/set-up-a-checkout.md) section in the Get started guide.
>
> **Show Universal Checkout**\\
>
> ACH via Stripe is automatically presented to the customer when calling `Primer.showUniversalCheckout`.
>
> ```javascript JS theme={"dark"}
> await Primer.showUniversalCheckout(clientToken);
> ```
>
> **Customization**\\
>
> Check the [customization guide](https://primer.io/docs/checkout/drop-in/customization.md) to learn how to customize Universal Checkout.
>
> **Go live**\\
>
> You don't need to do anything particular to go live — just make sure to use production credentials.
>
> #### Show Universal Checkout
>
> ACH via Stripe is automatically presented to the customer when calling `Primer.showUniversalCheckout`.
>
> ```typescript Typescript theme={"dark"}
> const CheckoutScreen = async (props: any) => {
>   const onUniversalCheckoutButtonTapped = async () => {
>     try {
>       await Primer.showUniversalCheckout(clientToken)
>     } catch (err) {
>       // handle error
>     }
>   }
> }
> ```
>
> #### Customization
>
> Check the [customization guide](https://primer.io/docs/checkout/drop-in/customization.md#styling-payment-method-button) to learn how to customize payment method buttons.

## Vaulting

When creating client session, `vaultOnAgreement` flag should be passed, along with `paymentType`:

```javascript JSX theme={"dark"}
"paymentMethod": {
    "vaultOnAgreement": true,
    "paymentType": "FIRST_PAYMENT"
  }
```

Note that the vaultOnAgreement flag is supported starting with API version 2.4.

In order to vault ACH via Stripe accounts, pass the following data in the Client Session:

| Parameter Name | Required | Description |
| - | - | - |
| [customerId](https://primer.io/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session.md#body-customer-id) | ✓ | A unique identifier for your customer |

> [tab] Web
>
> Minimum version of Web SDK: 2.47.0.
>
> ACH via Stripe payment tokens can be vaulted automatically on successful agreement if the `vaultOnAgreement` flag is turned on when creating the client session.
>
> ```javascript JSX theme={"dark"}
>
> "paymentMethod": {
>     "vaultOnAgreement": true,
>     "paymentType": "FIRST_PAYMENT"
>   }
> ```

> [tab] iOS
>
> Minimum version of iOS SDK: 2.33.0.
>
> ACH via Stripe payment tokens can be vaulted automatically on successful agreement if the `vaultOnAgreement` flag is turned on when creating the client session.
>
> ```javascript JSX theme={"dark"}
>
> "paymentMethod": {
>     "vaultOnAgreement": true,
>     "paymentType": "FIRST_PAYMENT"
>   }
> ```

> [tab] Android
>
> Minimum version of iOS SDK: 2.33.0.
>
> ACH via Stripe payment tokens can be vaulted automatically on successful agreement if the `vaultOnAgreement` flag is turned on when creating the client session.
>
> ```javascript JSX theme={"dark"}
>
> "paymentMethod": {
>     "vaultOnAgreement": true,
>     "paymentType": "FIRST_PAYMENT"
>   }
> ```

> [tab] React Native
>
> Minimum version of React Native SDK: 2.28.0.
>
> ACH via Stripe payment tokens can be vaulted automatically on successful agreement if the `vaultOnAgreement` flag is turned on when creating the client session.
>
> ```javascript JSX theme={"dark"}
>
> "paymentMethod": {
>     "vaultOnAgreement": true,
>     "paymentType": "FIRST_PAYMENT"
>   }
> ```

## Test

You can test payments using USD as the currency.

### Sandbox testing

* After selecting the payment method button in the Checkout, the page will navigate to a form
* After you complete the form, the Stripe pop-up will appear
* After finishing the steps inside the Stripe pop-up, the user will be taken back to the Checkout to confirm / decline mandate
* At the end of this flow, the payment status will be first PENDING in the Primer Dashboard and depending on the user actions or external factors, like insufficient funds in the bank account, it will be either DECLINED, CANCELLED or SETTLED for successful completion in Primer Dashboard
* To simulate the receiving of funds and transition the payment to SETTLED, you can choose in the Stripe pop-up: `Test Institution` and `Success` account in the next screen. Next you will be taken back to Checkout to confirm the mandate. Primer will receive a webhook and subsequently update the payment to SETTLED.

## Go Live

You don't need to do anything particular to go live — just make sure to use production credentials.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.