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

export const TxnTag = ({status}) => {
  const getStyles = status => {
    const baseStyle = {
      display: 'inline-block',
      padding: '4px 8px',
      borderRadius: '4px',
      fontSize: '13px',
      fontWeight: '400'
    };
    switch (status) {
      case "PENDING":
        return {
          ...baseStyle,
          backgroundColor: '#ececec',
          color: '#9f9f9f'
        };
      case "AUTHORIZED":
        return {
          ...baseStyle,
          backgroundColor: '#ecfdf5',
          color: '#047857'
        };
      case "SETTLED":
      case "PARTIALLY_SETTLED":
        return {
          ...baseStyle,
          backgroundColor: '#ecfdf5',
          color: '#047857'
        };
      case "DECLINED":
      case "FAILED":
        return {
          ...baseStyle,
          backgroundColor: '#fef2f2',
          color: '#dc2626'
        };
      case "CANCELLED":
        return {
          ...baseStyle,
          backgroundColor: '#fefce8',
          color: '#d78203'
        };
      case "SETTLING":
        return {
          ...baseStyle,
          backgroundColor: '#e0f2fe',
          color: '#0c4a6e'
        };
      default:
        return {
          ...baseStyle,
          backgroundColor: '#f3f4f6',
          color: '#374151'
        };
    }
  };
  return <span style={getStyles(status)}>{status}</span>;
};

## Before you begin

This guide assumes that you know how to

* [Show Drop-in Checkout](/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](/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session#body-currency-code)                                                                                                          | ✓        | 3-letter currency code in ISO 4217 format. For ACH via Stripe, only USD is supported.                                                                                                        |
| [order](/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session#body-order.)<br />[↳ lineItems](/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session#body-order-line-items) | ✓        | Details of the line items of the order                                                                                                                                                       |
| [metadata](/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session#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](/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session#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

<Tabs>
  <Tab title="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**<br />

    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](/docs/checkout/drop-in/customization#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>

  <Tab title="iOS">
    #### Pre-requisites

    **Pass device info when creating the client session**<br />

    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**<br />

    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**<br />

    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](/docs/checkout/drop-in/customization#styling-payment-method-button) to learn how to customize payment method buttons.
  </Tab>

  <Tab title="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 {
      /* Other dependencies... */
      // Use the latest version available (https://central.sonatype.com/search?q=primer&namespace=io.primer)
      implementation "io.primer:stripe-android:x.y.z"
    }
    ```

    **Define the Stripe publishable key and mandate information**<br />

    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](/docs/sdk/android/v2.x.x/primer/listeners/PrimerCheckoutListener#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.
    </Note>

    For a more comprehensive understanding, refer to the [documentation](/docs/sdk/android/v2.x.x/primer/listeners/PrimerCheckoutListener) available for the `PrimerCheckoutListener` interface and to the [Initialize Universal Checkout](/docs/get-started/set-up-a-checkout) 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](/docs/checkout/drop-in/customization#styling-payment-method-button) to learn how to customize payment method buttons.
  </Tab>

  <Tab title="React Native">
    #### Pre-requisites

    **Pass device info and payment type when creating the client session**<br />
    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**<br />
    Amend the dependencies section of your app's `build.gradle` to include Primer's `stripe-android` library:

    ```javascript JS theme={"dark"}
    dependencies {
      /* Other dependencies... */
      // Use the latest version available (https://central.sonatype.com/search?q=primer&namespace=io.primer)
      implementation "io.primer:stripe-android:x.y.z"
    }
    ```

    **iOS**<br />

    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](/docs/sdk/react-native/v2.x.x/primer/methods/configure#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.
    </Note>

    For a more comprehensive understanding, refer to the [configuration documentation](/docs/sdk/react-native/v2.x.x/primer/methods/configure) and to the [Initialize Universal Checkout](/docs/get-started/set-up-a-checkout) section in the Get started guide.

    **Show Universal Checkout**<br />
    ACH via Stripe is automatically presented to the customer when calling `Primer.showUniversalCheckout`.

    ```javascript JS theme={"dark"}
    await Primer.showUniversalCheckout(clientToken);
    ```

    **Customization**<br />
    Check the [customization guide](/docs/checkout/drop-in/customization) to learn how to customize Universal Checkout.

    **Go live**<br />
    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](/docs/checkout/drop-in/customization#styling-payment-method-button) to learn how to customize payment method buttons.
  </Tab>
</Tabs>

## 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](/docs/api-reference/v2.4/api-reference/client-session-api/create-a-client-session#body-customer-id) | ✓        | A unique identifier for your customer |

<Tabs>
  <Tab title="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>

  <Tab title="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>

  <Tab title="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>

  <Tab title="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"
      }
    ```
  </Tab>
</Tabs>

## 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 <TxnTag status="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 <TxnTag status="DECLINED" />, <TxnTag status="CANCELLED" /> or <TxnTag status="SETTLED" /> for successful completion in Primer Dashboard
* To simulate the receiving of funds and transition the payment to <TxnTag status="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 <TxnTag status="SETTLED" />.

## Go Live

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