# Accept an ACH Direct Debit payment

Build a custom payment form or use Stripe Checkout to accept payments with ACH Direct Debit.

# Android


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

Accepting ACH Direct Debit payments in your app consists of:

- Creating an object to track a payment
- Collecting payment method information with instant verifications enabled by [Stripe Financial Connections](https://docs.stripe.com/financial-connections.md)
- Submitting the payment to Stripe for processing
- Verifying your customer’s bank account

> ACH Direct Debit is a [delayed notification](https://docs.stripe.com/payments/payment-methods.md#payment-notification) payment method, which means that funds aren’t immediately available after payment. A payment typically takes 4 business days to arrive in your account.

Stripe uses the payment object, the [Payment Intent](https://docs.stripe.com/payments/payment-intents.md), to track and handle all the states of the payment until the payment completes.

The [Stripe Android SDK](https://github.com/stripe/stripe-android) is open source and [fully documented](https://stripe.dev/stripe-android/).

To install the SDK, add `stripe-android` and `financial-connections` to the `dependencies` block of your [app/build.gradle](https://developer.android.com/studio/build/dependencies) file:

#### Kotlin

```kotlin
plugins {
    id("com.android.application")
}

android { ... }

dependencies {
  // ...

  // Stripe Android SDK
  implementation("com.stripe:stripe-android:23.20.0")
  // Include the financial connections SDK to support US bank account as a payment method
  implementation("com.stripe:financial-connections:23.20.0")

  // Financial Connections SDK
  implementation("com.stripe:financial-connections:23.20.0")
}
```

> For details on the latest SDK release and past versions, see the [Releases](https://github.com/stripe/stripe-android/releases) page on GitHub. To receive notifications when a new release is published, [watch releases for the repository](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository).

Configure the SDK with your Stripe [publishable key](https://dashboard.stripe.com/apikeys) so that it can make requests to the Stripe API, such as in your `Application` subclass:

#### Kotlin

```kotlin
import com.stripe.android.PaymentConfiguration

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        PaymentConfiguration.init(
            applicationContext,
            "<<YOUR_PUBLISHABLE_KEY>>"
        )
    }
}
```

> Use your [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.

## Create or retrieve a customer [Recommended] [Server-side]

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

If your *merchant of record* (The legal entity responsible for facilitating the sale of products to a customer that handles any applicable regulations and liabilities, including sales taxes. In a Connect integration, it can be the platform or a connected account) is outside the US, make sure the Customer or customer-configured Account has your customer’s complete billing address before you collect their bank account details. The address can’t be a PO box. Provide the address when you create the object, or update a retrieved object if its address is missing or incomplete.

#### Accounts v2

Create a customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/object.md#v2_account_object-configuration-customer) object when your user creates an account with your business, or retrieve an existing `Account` associated with this user. Associating the ID of the `Account` object with your own internal representation of a customer enables you to retrieve and use the stored payment method details later. Include an email address on the `Account` to enable Financial Connections’ [return user optimization](https://docs.stripe.com/financial-connections/fundamentals.md#return-user-optimization).

For an `Account` with an `individual` entity type, provide the billing address using `identity.individual.address`:

```curl
curl -X POST https://api.stripe.com/v2/core/accounts \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-08-26.preview" \
  --json '{
    "contact_email": "{{CUSTOMER_EMAIL}}",
    "identity": {
        "country": "us",
        "entity_type": "individual",
        "individual": {
            "address": {
                "line1": "123 Main Street",
                "city": "San Francisco",
                "state": "CA",
                "postal_code": "94111",
                "country": "US"
            }
        }
    },
    "configuration": {
        "customer": {}
    }
  }'
```

For any other entity type, provide the billing address using `identity.business_details.address`:

```curl
curl -X POST https://api.stripe.com/v2/core/accounts \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-08-26.preview" \
  --json '{
    "contact_email": "{{CUSTOMER_EMAIL}}",
    "identity": {
        "country": "us",
        "entity_type": "company",
        "business_details": {
            "address": {
                "line1": "123 Main Street",
                "city": "San Francisco",
                "state": "CA",
                "postal_code": "94111",
                "country": "US"
            }
        }
    },
    "configuration": {
        "customer": {}
    }
  }'
```

#### Customers v1

Create a *Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) object when your user creates an account with your business, or retrieve an existing `Customer` associated with this user. Associating the ID of the `Customer` object with your own internal representation of a customer enables you to retrieve and use the stored payment method details later. Include an email address on the `Customer` to enable Financial Connections’ [return user optimization](https://docs.stripe.com/financial-connections/fundamentals.md#return-user-optimization).

```curl
curl https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d email={{CUSTOMER_EMAIL}} \
  -d "address[line1]=123 Main Street" \
  -d "address[city]=San Francisco" \
  -d "address[state]=CA" \
  -d "address[postal_code]=94111" \
  -d "address[country]=US"
```

## Create a PaymentIntent [Server-side] [Client-side]

A [PaymentIntent](https://docs.stripe.com/api/payment_intents.md) is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage.

### Server-side 

First, create a PaymentIntent on your server and specify the amount to collect and the `usd` currency. Optionally, specify the [id](https://docs.stripe.com/api/customers/object.md#customer_object-id) of the Customer.

If you want to reuse the payment method in the future, provide the [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage) parameter with the value of `off_session`.

For more information on Financial Connections fees, see [pricing details](https://stripe.com/financial-connections#pricing).

By default, collecting bank account payment information uses [Financial Connections](https://docs.stripe.com/financial-connections.md) to instantly verify your customer’s account, with a fallback option of manual account number entry and microdeposit verification. See the [Financial Connections docs](https://docs.stripe.com/financial-connections/ach-direct-debit-payments.md) to learn how to configure Financial Connections and access additional account data to optimize your ACH integration. For example, you can use Financial Connections to check an account’s balance before initiating the ACH payment.

> To expand access to additional data after a customer authenticates their account, they must re-link their account with expanded permissions.

#### 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 setup_future_usage=off_session \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}"
```

#### 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 setup_future_usage=off_session \
  -d "customer={{CUSTOMER_ID}}"
```

### Client-side 

Included in the returned PaymentIntent is a *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)), which the client side can use to securely complete the payment process instead of passing the entire PaymentIntent object. On the client, request a PaymentIntent from your server and use the client secret for subsequent API calls.

#### Kotlin

```kotlin
import androidx.appcompat.app.AppCompatActivity

class CheckoutActivity : AppCompatActivity() {
    private lateinit var paymentIntentClientSecret: String

    fun startCheckout() {
        // Request a PaymentIntent from your server and store its client secret
    }
}
```

## Collect payment method details [Client-side]

ACH Direct Debit requires a customer name and (optional) email for the payment to succeed. In your app, collect the required billing details from the customer:

- Their full name (first and last)
- Their email address

Use `CollectBankAccountConfiguration.USBankAccount` to create the parameters required to call `presentWithPaymentIntent`.

Initialize a `CollectBankAccountLauncher` instance inside onCreate of your checkout Activity, passing a method to handle the result. Then proceed to call `presentWithPaymentIntent` to collect bank account details, create a *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs), and attach that PaymentMethod to the PaymentIntent.

#### Kotlin

```kotlin
import androidx.appcompat.app.AppCompatActivity

class CheckoutActivity : AppCompatActivity() {
    private lateinit var paymentIntentClientSecret: String
    private lateinit var collectBankAccountLauncher CollectBankAccountLauncher

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Create collector
        collectBankAccountLauncher = CollectBankAccountLauncher.create(
            this
        ) { result: CollectBankAccountResult ->
            when (result) {
                is CollectBankAccountResult.Completed -> {
                    val intent = result.response.intent
                    if (intent.status === StripeIntent.Status.RequiresPaymentMethod) {
                        // Customer canceled the Financial Connections modal. Present them with other
                        // payment method type options.
                    } else if (intent.status === StripeIntent.Status.RequiresConfirmation) {
                        // We collected an account - possibly instantly verified, but possibly
                        // manually-entered. Display payment method details and mandate text
                        // to the customer and confirm the intent once they accept
                        // the mandate.
                    }
                }
                is CollectBankAccountResult.Cancelled -> {
                    // handle cancellation
                }
                is CollectBankAccountResult.Failed -> {
                    // handle error
                    print("Error: ${result.error}")
                }
            }
        }
    }

    fun startCheckout() {
        // ...

        // Build params
        val configuration: CollectBankAccountConfiguration =
            CollectBankAccountConfiguration.USBankAccount(
                name,
                email
            )

        // Calling this method will trigger the Financial Connections modal to be displayed
        collectBankAccountLauncher.presentWithPaymentIntent(
            publishableKey,
            paymentIntentClientSecret,
            collectParams
        )
    }
}
```

This loads a modal UI that handles bank account details collection and verification. When it completes, the *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) automatically attaches to the PaymentIntent.

## Optional: Customize the sheet [Client-side]

### Dark mode

By default, `CollectBankAccountLauncher` automatically adapts to the user’s system-wide appearance settings (light and dark mode). You can change this by setting light or dark mode on your app:

#### Kotlin

```kotlin
// force dark
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
// force light
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
```

## Optional: Access data on a Financial Connections bank account [Server-side]

You can only access Financial Connections data if you request additional [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) when you create your PaymentIntent .

After your customer successfully completes the [Stripe Financial Connections authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow), the `us_bank_account` PaymentMethod returned includes a [financial_connections_account](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-us_bank_account-financial_connections_account) ID that points to a [Financial Connections Account](https://docs.stripe.com/api/financial_connections/accounts.md). Use this ID to access account data.

> Bank accounts that your customers link through manual entry and microdeposits don’t have a `financial_connections_account` ID on the Payment Method.

To determine the Financial Connections account ID, retrieve the PaymentIntent and expand the `payment_method` attribute:

```curl
curl -G https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "expand[]=payment_method"
```

```json
{
  "id": "{{PAYMENT_INTENT_ID}}",
  "object": "payment_intent",
  // ...
  "payment_method": {
    "id": "{{PAYMENT_METHOD_ID}}",
    // ...
    "type": "us_bank_account"
    "us_bank_account": {
      "account_holder_type": "individual",
      "account_type": "checking",
      "bank_name": "TEST BANK",
      "financial_connections_account": "{{FINANCIAL_CONNECTIONS_ACCOUNT_ID}}",
      "fingerprint": "q9qchffggRjlX2tb",
      "last4": "6789",
      "routing_number": "110000000"
    }
  }
  // ...
}

```

If you opted to receive `balances` permissions, we recommend checking a balance at this stage to verify sufficient funds before confirming a payment.

Learn more about using additional account data to optimize your ACH integration with [Financial Connections](https://docs.stripe.com/financial-connections/ach-direct-debit-payments.md#optimize).

## Collect mandate acknowledgement and submit the payment [Client-side]

Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.

To be compliant with *Nacha* (Nacha is the governing body that oversees the ACH network) rules, you must obtain authorization from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see [Mandates](https://docs.stripe.com/payments/ach-direct-debit.md#mandates).

When the customer accepts the mandate terms, you must confirm the Payment. Use `confirm` to complete the payment when the customer submits the form.

#### Kotlin

```kotlin
class CheckoutActivity : AppCompatActivity() {
    // ...
    private lateinit var paymentIntentClientSecret: String
    private val paymentLauncher: PaymentLauncher by lazy {
        val paymentConfiguration = PaymentConfiguration.getInstance(applicationContext)
        PaymentLauncher.Companion.create(
            this,
            paymentConfiguration.publishableKey,
            paymentConfiguration.stripeAccountId,
            ::onPaymentResult
        )
    }

    private fun startCheckout() {
        // ...

        val confirmParams = ConfirmPaymentIntentParams.create(
            clientSecret = paymentIntentClientSecret,
            paymentMethodType = PaymentMethod.Type.USBankAccount
        )
        paymentLauncher.confirm(confirmParams)
    }

    private fun onPaymentResult(paymentResult: PaymentResult) {
        when (paymentResult) {
            is PaymentResult.Completed -> {
                // show success UI
            }
            is PaymentResult.Canceled -> {
                // handle cancel flow
            }
            is PaymentResult.Failed -> {
                // handle failures
                // (for example, the customer might need to choose a new payment
                // method)
            }
        }
    }
}
```

> `confirm` might take several seconds to complete. During that time, disable resubmittals of your form and show a waiting indicator (for example, a spinner). If you receive an error, show it to the customer, re-enable the form, and hide the waiting indicator.

If successful, Stripe returns a PaymentIntent object, with one of the following possible statuses:

| Status            | Description                                                              | Next Steps                                                                                                                                                                                       |
| ----------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `requires_action` | Further action is needed to complete bank account verification.          | Step 5: [Verifying bank accounts with microdeposits](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md?payment-ui=mobile&platform=android#android-verify-with-microdeposits) |
| `processing`      | The bank account was instantly verified or verification isn’t necessary. | Step 6: [Confirm the PaymentIntent succeeded](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md?payment-ui=mobile&platform=android#android-confirm-paymentintent-succeeded)  |

After successfully confirming the Payment, an email confirmation of the mandate and collected bank account details must be sent to your customer. Stripe handles these by default, but you can choose to [send custom notifications](https://docs.stripe.com/payments/ach-direct-debit.md#mandate-and-microdeposit-emails) if you prefer.

## Verify bank account with micro-deposits [Client-side]

Not all customers can verify the bank account instantly. This step only applies if your customer has elected to opt out of the instant verification flow in the previous step.

In these cases, Stripe sends a `descriptor_code` microdeposit and might fall back to an `amount` microdeposit if any further issues arise with verifying the bank account. These deposits take 1-2 business days to appear on the customer’s online statement.

- **Descriptor code**. Stripe sends a single, 0.01 USD microdeposit to the customer’s bank account with a unique, 6-digit `descriptor_code` that starts with SM. Your customer uses this string to verify their bank account.
- **Amount**. Stripe sends two, non-unique microdeposits to the customer’s bank account, with a statement descriptor that reads `ACCTVERIFY`. Your customer uses the deposit amounts to verify their bank account.

The result of the `confirmPayment` method call in the previous step is a PaymentIntent in the `requires_action` state. The PaymentIntent contains a [next_action](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-verify_with_microdeposits) field that contains some useful information for completing the verification.

If you supplied a [billing email](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-billing_details-email), Stripe notifies your customer through this email when the deposits are expected to arrive. The email includes a link to a Stripe-hosted verification page where they can confirm the amounts of the deposits and complete verification.

> Verification attempts have a limit of ten failures for descriptor-based microdeposits and three for amount-based ones. If you exceed this limit, we can no longer verify the bank account. In addition, microdeposit verifications have a timeout of 10 days. If you can’t verify microdeposits in that time, the PaymentIntent reverts to requiring new payment method details. Clear messaging about what these microdeposits are and how you use them can help your customers avoid verification issues.

### Optional: Send custom email notifications

You can also send [custom email notifications](https://docs.stripe.com/payments/ach-direct-debit.md#mandate-and-microdeposit-emails) to your customer. After you set up custom emails, you need to specify how the customer responds to the verification email. To do so, choose *one* of the following:

- Use the Stripe-hosted verification page. To do this, use the `verify_with_microdeposits[hosted_verification_url]` URL in the [next_action](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-verify_with_microdeposits-hosted_verification_url) object to direct your customer to complete the verification process.

- If you prefer not to use the Stripe-hosted verification page, create a form in your app. Your customers then use this form to relay microdeposit amounts to you and verify the bank account using the Android SDK.

  - At minimum, set up the form to handle the `descriptor code` parameter, which is a 6-digit string for verification purposes.
  - Stripe also recommends that you set your form to handle the `amounts` parameter, as some banks your customers use might require it.

  Integrations only pass in the `descriptor_code` *or* `amounts`. To determine which one your integration uses, check the value for `verify_with_microdeposits[microdeposit_type]` in the `next_action` object.

#### Kotlin

```kotlin
// Use if you are using a descriptor code, do not use if you are using amounts
fun verifyPaymentIntentWithMicrodeposits(
    clientSecret: String,
    descriptorCode: String,
    callback: ApiResultCallback<PaymentIntent>
)

// Use if you are using amounts, do not use if you are using descriptor code
fun verifyPaymentIntentWithMicrodeposits(
    clientSecret: String,
    firstAmount: Int,
    secondAmount: Int,
    callback: ApiResultCallback<PaymentIntent>
)
```

When the bank account is successfully verified, Stripe returns the PaymentIntent object with a `status` of `processing`.

Verification can fail for several reasons. The failure happens synchronously as a direct error response.

```json
{
  "error": {
    "code": "payment_method_microdeposit_verification_amounts_mismatch",
    "message": "The amounts provided do not match the amounts that were sent to the bank account. You have {attempts_remaining} verification attempts remaining.",
    "type": "invalid_request_error"
  }
}
```

| Error Code                                                   | Message                                                                                                                                         | Status change                                                           |
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `payment_method_microdeposit_failed`                         | Microdeposits failed. Please check the account, institution and transit numbers provided.                                                       | `status` is `requires_payment_method`, and `last_payment_error` is set. |
| `payment_method_microdeposit_verification_amounts_mismatch`  | The amounts provided don’t match the amounts that were sent to the bank account. You have {attempts_remaining} verification attempts remaining. | Unchanged                                                               |
| `payment_method_microdeposit_verification_attempts_exceeded` | Exceeded number of allowed verification attempts                                                                                                | `status` is `requires_payment_method`, and `last_payment_error` is set. |

## Confirm the PaymentIntent succeeded [Server-side]

ACH Direct Debit is a [delayed notification](https://docs.stripe.com/payments/payment-methods.md#payment-notification) payment method. This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of `processing`. After the payment has succeeded, the PaymentIntent status is updated from `processing` to `succeeded`.

We recommend using [webhooks](https://docs.stripe.com/payments/payment-intents/verifying-status.md#webhooks) to *confirm* (Confirming an intent indicates that the customer intends to use the current or provided payment method. Upon confirmation, the intent attempts to initiate the portions of the flow that have real-world side effects) the charge has succeeded and to notify the customer that the payment is complete. You can also view events on the [Stripe Dashboard](https://dashboard.stripe.com/events).

#### PaymentIntent events

The following events are sent when the PaymentIntent status is updated:

| Event                           | Description                                                                                     | Next Step                                                                                                                                                                                                                                                                                     |
| ------------------------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment_intent.processing`     | The customer’s payment was submitted to Stripe successfully.                                    | Wait for the initiated payment to succeed or fail.                                                                                                                                                                                                                                            |
| `payment_intent.succeeded`      | The customer’s payment succeeded.                                                               | Fulfill the goods or services that were purchased.                                                                                                                                                                                                                                            |
| `payment_intent.payment_failed` | The customer’s payment was declined. This can also apply to a failed microdeposit verification. | Contact the customer through email or push notification and request another payment method. If the webhook was sent due to a failed microdeposit verification, the user needs to enter in their bank account details again and a new set of microdeposits will be deposited in their account. |

#### Charge events

You may also use additional Charge webhooks to track the payment’s status. Upon receiving the `charge.updated` webhook, the payment is no longer cancellable.

The following events are sent when the Charge status is updated:

| Event              | Description                                                                                                                                     | Next Step                                                                         |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `charge.pending`   | The customer’s payment was created successfully.                                                                                                | Wait for the initiated payment to be processed.                                   |
| `charge.updated`   | The customer’s payment was updated. It can be emitted when a new balance transaction was created, a charge description or metadata got updated. | Wait for the initiated payment to succeed or fail.                                |
| `charge.succeeded` | The customer’s payment succeeded and the funds are available in your balance.                                                                   | Fulfill the goods or services that were purchased.                                |
| `charge.failed`    | The customer’s payment failed.                                                                                                                  | Check the charge’s failure_code and failure_message to determine further actions. |

## Test your integration

Learn how to test scenarios with instant verifications using [Financial Connections](https://docs.stripe.com/financial-connections/testing.md#web-how-to-use-test-accounts).

### Send transaction emails in a sandbox

After you collect the bank account details and accept a mandate, send the mandate confirmation and microdeposit verification emails in a *sandbox* (A sandbox is an isolated test environment that allows you to test Stripe functionality in your account without affecting your live integration. Use sandboxes to safely experiment with new features and changes).

If your domain is **{domain}** and your username is **{username}**, use the following email format to send test transaction emails: **{username}+test\_email@{domain}**.

For example, if your domain is **example.com** and your username is **info**, use the format **info+test\_email@example.com** for testing ACH Direct Debit payments. This format ensures that emails route correctly. If you don’t include the **+test\_email** suffix, Stripe won’t send the email.

> You must [set up your Stripe account](https://docs.stripe.com/get-started/account/set-up.md) before you can trigger these emails while testing.

### Test account numbers

Stripe provides several test account numbers and corresponding tokens you can use to make sure your integration for manually-entered bank accounts is ready for production.

| Account number | Token                                  | Routing number | Behavior                                                                                                                                              |
| -------------- | -------------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `000123456789` | `pm_usBankAccount_success`             | `110000000`    | The payment succeeds.                                                                                                                                 |
| `000111111113` | `pm_usBankAccount_accountClosed`       | `110000000`    | The payment fails because the account is closed.                                                                                                      |
| `000000004954` | `pm_usBankAccount_riskLevelHighest`    | `110000000`    | The payment is blocked by Radar due to a [high risk of fraud](https://docs.stripe.com/radar/transaction-risk-prevention.md#high-risk).                |
| `000111111116` | `pm_usBankAccount_noAccount`           | `110000000`    | The payment fails because no account is found.                                                                                                        |
| `000222222227` | `pm_usBankAccount_insufficientFunds`   | `110000000`    | The payment fails due to insufficient funds.                                                                                                          |
| `000333333335` | `pm_usBankAccount_debitNotAuthorized`  | `110000000`    | The payment fails because debits aren’t authorized.                                                                                                   |
| `000444444440` | `pm_usBankAccount_invalidCurrency`     | `110000000`    | The payment fails due to invalid currency.                                                                                                            |
| `000666666661` | `pm_usBankAccount_failMicrodeposits`   | `110000000`    | The payment fails to send microdeposits.                                                                                                              |
| `000555555559` | `pm_usBankAccount_dispute`             | `110000000`    | The payment triggers a dispute.                                                                                                                       |
| `000000000009` | `pm_usBankAccount_processing`          | `110000000`    | The payment stays in processing indefinitely. Useful for testing [PaymentIntent cancellation](https://docs.stripe.com/api/payment_intents/cancel.md). |
| `000777777771` | `pm_usBankAccount_weeklyLimitExceeded` | `110000000`    | The payment fails due to payment amount causing the account to exceed its weekly payment volume limit.                                                |
| `000888888885` |                                        | `110000000`    | The payment fails because of a deactivated [tokenized account number](https://docs.stripe.com/financial-connections/tokenized-account-numbers.md).    |

Before test transactions can complete, you need to verify all test accounts that automatically succeed or fail the payment. To do so, use the test microdeposit amounts or descriptor codes in the following table.

### Test microdeposit amounts and descriptor codes

To mimic different scenarios, use these microdeposit amounts or 0.01 descriptor code values.

| Microdeposit values | 0.01 descriptor code values | Scenario                                                         |
| ------------------- | --------------------------- | ---------------------------------------------------------------- |
| `32` and `45`       | SM11AA                      | Simulates verifying the account.                                 |
| `10` and `11`       | SM33CC                      | Simulates exceeding the number of allowed verification attempts. |
| `40` and `41`       | SM44DD                      | Simulates a microdeposit timeout.                                |

### Test settlement behavior

Test transactions settle instantly and are added to your available test balance. This behavior differs from live mode, where transactions can take [multiple days](https://docs.stripe.com/payments/ach-direct-debit.md#timing) to settle in your available balance.

## Optional: Instant only verification [Server-side]

By default, US bank account payments allows your customers to use instant bank account verification or microdeposits. You can optionally require instant bank account verification only, using the [verification_method](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-us_bank_account-verification_method) parameter when you create the PaymentIntent.

#### Accounts v2

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d setup_future_usage=off_session \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d "automatic_payment_methods[enabled]=true" \
  -d "payment_method_options[us_bank_account][verification_method]=instant" \
  -d "payment_method_options[us_bank_account][financial_connections][permissions][]=payment_method" \
  -d "payment_method_options[us_bank_account][financial_connections][permissions][]=balances"
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d setup_future_usage=off_session \
  -d "customer={{CUSTOMER_ID}}" \
  -d "automatic_payment_methods[enabled]=true" \
  -d "payment_method_options[us_bank_account][verification_method]=instant" \
  -d "payment_method_options[us_bank_account][financial_connections][permissions][]=payment_method" \
  -d "payment_method_options[us_bank_account][financial_connections][permissions][]=balances"
```

This ensures that you don’t need to handle microdeposit verification. However, if instant verification fails, the PaymentIntent’s status is `requires_payment_method`, indicating a failure to instantly verify a bank account for your customer.

## Optional: Microdeposit only verification [Server-side]

By default, US bank account payments allow your customers to use instant bank account verification or microdeposits. You can optionally require microdeposit verification only using the [verification_method](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-us_bank_account-verification_method) parameter when you create the PaymentIntent.

> If using a custom payment form, you must build your own UI to collect bank account details. If you disable Stripe microdeposit emails, you must build your own UI for your customer to confirm the microdeposit code or amount.

#### Accounts v2

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d setup_future_usage=off_session \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d "automatic_payment_methods[enabled]=true" \
  -d "payment_method_options[us_bank_account][verification_method]=microdeposits"
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d setup_future_usage=off_session \
  -d "customer={{CUSTOMER_ID}}" \
  -d "automatic_payment_methods[enabled]=true" \
  -d "payment_method_options[us_bank_account][verification_method]=microdeposits"
```

You must then collect your customer’s bank account with your own form and call [stripe.confirmUsBankAccountPayment](https://docs.stripe.com/js/payment_intents/confirm_us_bank_account_payment) with those details to complete the PaymentIntent.

```javascript
var form = document.getElementById('payment-form');
var accountholderName = document.getElementById('accountholder-name');
var email = document.getElementById('email');
var accountNumber = document.getElementById('account-number');
var routingNumber = document.getElementById('routing-number');
var accountHolderType= document.getElementById('account-holder-type');

var submitButton = document.getElementById('submit-button');
var clientSecret = submitButton.dataset.secret;

form.addEventListener('submit', function(event) {
  event.preventDefault();

  stripe.confirmUsBankAccountPayment(clientSecret, {
    payment_method: {
      billing_details: {
        name: accountholderName.value,
        email: email.value,
      },
      us_bank_account: {
        account_number: accountNumber.value,
        routing_number: routingNumber.value,
        account_holder_type: accountHolderType.value, // 'individual' or 'company'
      },
    },
  })
  .then(({paymentIntent, error}) => {
    if (error) {
      // Inform the customer that there was an error.
      console.log(error.message);
    } else {
      // Handle next step based on the intent's status.
      console.log("PaymentIntent ID: " + paymentIntent.id);
      console.log("PaymentIntent status: " + paymentIntent.status);
    }
  });
});
```

## Optional: Payment Reference

The payment reference number is a bank-generated value that allows the bank account owner to use their bank to locate funds. When the payment succeeds, Stripe provides the payment reference number in the Dashboard and inside the [Charge object](https://docs.stripe.com/api/charges/object.md).

| Charge State | Payment Reference Value                  |
| ------------ | ---------------------------------------- |
| Pending      | Unavailable                              |
| Failed       | Unavailable                              |
| Succeeded    | Available (for example, 091000015001234) |

In addition, when you receive the `charge.succeeded` webhook, view the content of `payment_method_details` to locate the [payment_reference](https://docs.stripe.com/api/charges/object.md#charge_object-payment_method_details-us_bank_account-payment_reference).

The following example event shows the rendering of a successful ACH payment with a payment reference number.

#### charge-succeeded

```json
{
  "id": "{{EVENT_ID}}",
  "object": "event",
  // omitted some fields in the example
  "type": "charge.succeeded",
  "data": {
    "object": {
      "id": "{{PAYMENT_ID}}",
      "object": "charge",
      //...
    "paid": true,
    "payment_intent": "{{PAYMENT_INTENT_ID}}",
    "payment_method": "{{PAYMENT_METHOD_ID}}",
    "payment_method_details": {
      "type": "us_bank_account",
      "us_bank_account": {
        "account_holder_type": "individual",
        "account_type": "checking",
        "bank_name": "TEST BANK",
        "fingerprint": "Ih3foEnRvLXShyfB",
        "last4": "1000",
        "payment_reference": "091000015001234",
        "routing_number": "110000000"
        }
      }
      // ...
    }
  }
}
```

View the contents of the `destination_details` to locate the [refund reference](https://docs.stripe.com/api/refunds/object.md#refund_object-destination_details-us_bank_transfer-reference) associated with the refunded ACH payments.

The following example event shows the rendering of a successful ACH refund with a refund reference number. Learn more about [refunds](https://docs.stripe.com/refunds.md).

#### charge-refund-updated

```json
{
  "id": "{{EVENT_ID}}",
  "object": "event",
  "type": "charge.refund.updated",
  "data": {
    "object": {
      "id": "{{REFUND_ID}}",
      "object": "refund",
      //...
    "payment_intent": "{{PAYMENT_INTENT_ID}}",
    "destination_details": {
      "type": "us_bank_transfer",
      "us_bank_transfer": {
        "reference": "091000015001111",
        "reference_status": "available"
        }
      }
      // ...
    }
  }
}
```

## See also

- [Save ACH Direct Debit pre-authorized debit details for future payments](https://docs.stripe.com/payments/ach-direct-debit/set-up-payment.md)

