# Accept an ACH Direct Debit payment Build a custom payment form or use Stripe Checkout to accept payments with ACH Direct Debit. # Checkout > Stripe can automatically present the relevant payment methods to your customers by evaluating currency, payment method restrictions, and other parameters. > > - Follow the [Accept a payment](https://docs.stripe.com/payments/accept-a-payment.md?payment-ui=checkout&ui=stripe-hosted) guide to build a Checkout integration that uses [dynamic payment methods](https://docs.stripe.com/payments/payment-methods/dynamic-payment-methods.md). - If you don’t want to use dynamic payment methods, follow the steps below to manually configure the payment methods in your Checkout integration. Stripe users in the US can use Checkout in payment mode to accept ACH Direct Debit payments. A Checkout Session represents the details of your customer’s intent to purchase. You create a Session when your customer wants to pay for something. After redirecting your customer to a Checkout Session, Stripe presents a payment form where your customer can complete their purchase. After your customer completes a purchase, they redirect back to your site. With Checkout, you can create a Checkout Session with `us_bank_account` as a payment method type to track and handle the states of the payment until the payment completes. > 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. ## Determine compatibility **Supported business locations**: US **Supported currencies**: `usd` **Presentment currencies**: `usd` **Payment mode**: Yes **Setup mode**: Yes **Subscription mode**: Yes To support ACH Direct Debit payments, make sure you express *Prices* (Prices define how much and how often to charge for products. This includes how much the product costs, what currency to use, and the interval if the price is for subscriptions) for all line items in US dollars (currency code `usd`). ## 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 [specify a preview version](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning) in your code. > > To request access to the Accounts v2 preview, > > For most use cases, we recommend [modeling your customers as customer-configured Account objects](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md) instead of using [Customer](https://docs.stripe.com/api/customers.md) objects. #### Accounts v2 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). ```curl curl -X POST https://api.stripe.com/v2/core/accounts \ -H "Authorization: Bearer <>" \ -H "Stripe-Version: 2026-05-27.preview" \ --json '{ "contact_email": "{{CUSTOMER_EMAIL}}" }' ``` #### 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 "<>:" \ -d email={{CUSTOMER_EMAIL}} ``` ## Accept a payment > Build an integration to [accept a payment](https://docs.stripe.com/payments/accept-a-payment.md?integration=checkout) with Checkout before using this guide. This guides you through enabling ACH Direct Debit and shows the differences between accepting payments using dynamic payment methods and manually configuring payment methods. ### Enable ACH Direct Debit as a payment method When creating a new [Checkout Session](https://docs.stripe.com/api/checkout/sessions.md), you need to: 1. Add `us_bank_account` to the list of `payment_method_types`. 1. Make sure all your `line_items` use the `usd` currency. #### Stripe-hosted page #### Accounts v2 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "line_items[0][price_data][currency]=usd" \ -d "line_items[0][price_data][product_data][name]=T-shirt" \ -d "line_items[0][price_data][unit_amount]=2000" \ -d "line_items[0][quantity]=1" \ -d mode=payment \ -d "payment_method_types[0]=card" \ -d "payment_method_types[1]=us_bank_account" \ --data-urlencode "success_url=https://example.com/success" ``` #### Customers v1 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d "customer={{CUSTOMER_ID}}" \ -d "line_items[0][price_data][currency]=usd" \ -d "line_items[0][price_data][product_data][name]=T-shirt" \ -d "line_items[0][price_data][unit_amount]=2000" \ -d "line_items[0][quantity]=1" \ -d mode=payment \ -d "payment_method_types[0]=card" \ -d "payment_method_types[1]=us_bank_account" \ --data-urlencode "success_url=https://example.com/success" ``` #### Full embedded page #### Accounts v2 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "line_items[0][price_data][currency]=usd" \ -d "line_items[0][price_data][product_data][name]=T-shirt" \ -d "line_items[0][price_data][unit_amount]=2000" \ -d "line_items[0][quantity]=1" \ -d mode=payment \ -d "payment_method_types[0]=card" \ -d "payment_method_types[1]=us_bank_account" \ --data-urlencode "return_url=https://example.com/return" \ -d ui_mode=embedded_page ``` #### Customers v1 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d "customer={{CUSTOMER_ID}}" \ -d "line_items[0][price_data][currency]=usd" \ -d "line_items[0][price_data][product_data][name]=T-shirt" \ -d "line_items[0][price_data][unit_amount]=2000" \ -d "line_items[0][quantity]=1" \ -d mode=payment \ -d "payment_method_types[0]=card" \ -d "payment_method_types[1]=us_bank_account" \ --data-urlencode "return_url=https://example.com/return" \ -d ui_mode=embedded_page ``` 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. If the customer opts for microdeposit verification instead of Financial Connections, Stripe automatically sends two small deposits to the provided bank account. These deposits can take 1-2 business days to appear on the customer’s online bank statement. When the deposits are expected to arrive, the customer receives an email with a link to confirm these amounts and verify the bank account with Stripe. After verification is complete, the payment begins processing. We recommend including the [payment_intent_data.setup_future_usage](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-setup_future_usage) parameter with a value of `off_session` when creating a payment mode Session for ACH Direct Debit so you can [save payment method details](https://docs.stripe.com/payments/accept-a-payment.md?payment-ui=checkout&ui=stripe-hosted#save-payment-method-details). ### Fulfill your orders After accepting a payment, learn how to [fulfill orders](https://docs.stripe.com/checkout/fulfillment.md). ## 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, we 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/risk-evaluation.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 below. ### 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 livemode, where transactions can take [multiple days](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#timing) to settle in your available balance. ## Additional considerations ### Microdeposit verification failure When a bank account is pending verification with microdeposits, the customer can fail to verify for three reasons: - The microdeposits failed to send to the customer’s bank account (this usually indicates a closed or unavailable bank account or incorrect bank account number). - The customer made 10 failed verification attempts for the account. Exceeding this limit means the bank account can no longer be verified or reused. - The customer failed to verify the bank account within 10 days. If the bank account fails verification for one of these reasons, you can [handle the `checkout.session.async_payment_failed` event](https://docs.stripe.com/api/events/types.md?event_types-invoice.payment_succeeded=#event_types-checkout.session.async_payment_failed) to contact the customer about placing a new order. ## Optional: Instant only verification By default, ACH Direct Debit payments allow your customers to use instant bank account verification or microdeposits. You can optionally require instant bank account verification using the `payment_method_options[us_bank_account][verification_method]` parameter when you create the Checkout Session. #### Accounts v2 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d mode=payment \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "payment_method_types[0]=card" \ -d "payment_method_types[1]=us_bank_account" \ -d "payment_method_options[us_bank_account][verification_method]=instant" \ -d "payment_method_options[us_bank_account][financial_connections][permissions][0]=payment_method" \ -d "line_items[0][price_data][currency]=usd" \ -d "line_items[0][price_data][unit_amount]=2000" \ -d "line_items[0][price_data][product_data][name]=T-shirt" \ -d "line_items[0][quantity]=1" \ --data-urlencode "success_url=https://example.com/success" ``` #### Customers v1 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d mode=payment \ -d "customer={{CUSTOMER_ID}}" \ -d "payment_method_types[0]=card" \ -d "payment_method_types[1]=us_bank_account" \ -d "payment_method_options[us_bank_account][verification_method]=instant" \ -d "payment_method_options[us_bank_account][financial_connections][permissions][0]=payment_method" \ -d "line_items[0][price_data][currency]=usd" \ -d "line_items[0][price_data][unit_amount]=2000" \ -d "line_items[0][price_data][product_data][name]=T-shirt" \ -d "line_items[0][quantity]=1" \ --data-urlencode "success_url=https://example.com/success" ``` ## Optional: Access data on a Financial Connections bank account 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 Checkout 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 Checkout Session and expand the `payment_intent.payment_method` attribute: ```curl curl -G https://api.stripe.com/v1/checkout/sessions/{{CHECKOUTSESSION_ID}} \ -u "<>:" \ -d "expand[]=payment_intent.payment_method" ``` ```json { "id": "{{CHECKOUT_SESSION_ID}}", "object": "checkout.session", // ... "payment_intent": { "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" } } // ... } } ``` 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). ## Optional: Resolve disputes [Server-side] Customers can generally [dispute an ACH Direct Debit payment](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#resolving-disputes) through their bank for up to 60 calendar days after a debit on a personal account, or up to 2 business days for a business account. In rare instances, a customer might be able to successfully dispute a debit payment outside these standard dispute timelines. When a customer disputes a payment, Stripe sends a [charge.dispute.closed](https://docs.stripe.com/api/events/types.md#event_types-charge.dispute.closed) webhook event, and the PaymentMethod authorization is revoked. In rare situations, Stripe might receive an ACH failure from the bank after a PaymentIntent has transitioned to `succeeded`. If this happens, Stripe creates a dispute with a `reason` of: - `insufficient_funds` - `incorrect_account_details` - `bank_can't_process` Stripe charges a failure fee in this situation. Future payments reusing this PaymentMethod return the following error: ```javascript { "error": { "message": "This PaymentIntent requires a mandate, but no existing mandate was found. Collect mandate acceptance from the customer and try again, providing acceptance data in the mandate_data parameter.", "payment_intent": { ... } "type": "invalid_request_error" } } ``` This error contains a PaymentIntent in the `requires_confirmation` state. To continue with the payment, you must: 1. Resolve the dispute with the customer to ensure future payments won’t be disputed. 1. Confirm authorization from your customer again. To confirm authorization for the payment, you can [collect mandate acknowledgement from your customer online with Stripe.js](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md?platform=web#web-collect-mandate-and-submit) or confirm authorization with your customer offline using the Stripe API. > If a customer disputes more than one payment from the same bank account, Stripe blocks their bank account. Contact [Stripe Support](https://support.stripe.com/?contact=true) for further resolution. ```curl curl https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/confirm \ -u "<>:" \ -d "mandate_data[customer_acceptance][type]=offline" ``` ## 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" } } // ... } } } ``` ## Optional: Configure customer debit date [Server-side] You can control the date that Stripe debits a customer’s bank account using the [target date](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-payment_method_options-us_bank_account-target_date). The target date must be at least three days in the future and no more than 15 days from the current date. The target date schedules money to leave the customer’s account on the target date. Target dates that meet one of the following criteria delay the debit until the next available business day: - Target date falls on a weekend, a bank holiday, or other non-business day. - Target date is fewer than three business days in the future. This parameter operates on a best-effort basis. Each customer’s bank might process debits on different dates, depending on local bank holidays or other reasons. When you use a target date, the PaymentIntent’s status transitions to `processing` as soon as you confirm the PaymentIntent. The PaymentIntent’s status transitions to `succeeded` after the bank settles the payment, or `requires_payment_method` if the bank rejects the debit. You can track the `expected_debit_date` in the associated Charge’s [payment_method_details](https://docs.stripe.com/api/charges/object.md#charge_object-payment_method_details) through the [charge.updated](https://docs.stripe.com/api/events/types.md#event_types-charge.updated) webhook event. > You can’t set the [verification method](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-payment_method_options-us_bank_account-verification_method) to `microdeposits` when using a [target date](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-payment_method_options-us_bank_account-target_date), as the verification process could take longer than the target date, causing payments to arrive later than expected. ## 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)