# Accept an ACH Direct Debit payment
Build a custom payment form or use Stripe Checkout to accept payments with ACH Direct Debit.
# Payment Intents API
To determine which API meets your business needs, see the [comparison guide](https://docs.stripe.com/payments/checkout-sessions-and-payment-intents-comparison.md).
Use the [Payment Element](https://docs.stripe.com/payments/payment-element.md) to embed a custom Stripe payment form in your website or application and offer payment methods to customers. For advanced configurations and customisations, refer to the [Accept a Payment](https://docs.stripe.com/payments/accept-a-payment.md) integration guide.
Before you use ACH direct debit in your Payment Element integration, learn about the following ACH direct debit-specific considerations:
- Learn about [mandate collection](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#mandate-collection).
- Choose how you [verify bank accounts](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#bank-verification).
### Mandate collection
When accepting ACH payments, you need to understand mandates.
Unless you use a direct API integration, Stripe handles mandate collection and storage for your business to make sure that regulatory requirements are met. When a customer accepts the mandate during the payment process, Stripe automatically stores this information and it becomes available for you to access from your Dashboard.
## Set up Stripe [Server-side]
To get started, [create a Stripe account](https://dashboard.stripe.com/register).
Use our official libraries for access to the Stripe API from your application:
#### Ruby
```bash
# Available as a gem
sudo gem install stripe
```
```ruby
# If you use bundler, you can add this line to your Gemfile
gem 'stripe'
```
## Collect payment details [Client-side]
Use the Payment Element to collect payment details on the client. The Payment Element is a pre-built UI component that simplifies collecting payment details for various payment methods.
The Payment Element contains an iframe that securely sends payment information to Stripe over an HTTPS connection. Avoid placing the Payment Element within another iframe because some payment methods require redirecting to another page for payment confirmation.
The checkout page address must start with `https://` rather than `http://` for your integration to work. You can test your integration without using HTTPS, but remember to [enable it](https://docs.stripe.com/security/guide.md#tls) when you’re ready to accept live payments.
#### HTML + JS
### Set up Stripe.js
The Payment Element is automatically available as a feature of Stripe.js. Include the Stripe.js script on your checkout page by adding it to the `head` of your HTML file. Always load Stripe.js directly from js.stripe.com to remain PCI compliant. Don’t include the script in a bundle or host a copy of it yourself.
```html
Checkout
```
Create an instance of Stripe with the following JavaScript on your checkout page:
```javascript
// Set your publishable key: remember to change this to your live publishable key in production
// See your keys here: https://dashboard.stripe.com/apikeys
const stripe = Stripe('<>');
```
### Add the Payment Element to your checkout page
To add the Payment Element to your checkout page, create an empty DOM node (container) with a unique ID in your payment form:
```html
```
You can manage payment methods from the Dashboard or manually list the payment methods you want to be available when you create the [Payment Element](https://docs.stripe.com/js/elements_object/create_without_intent).
#### Manage payment methods from the Dashboard
```javascript
const options = {mode: 'payment',
amount: 1099,
currency: 'usd',
// Fully customizable with appearance API.
appearance: {/*...*/},
};
// Set up Stripe.js and Elements to use in checkout formconst elements = stripe.elements(options);
// Create and mount the Payment Element
const paymentElementOptions = { layout: 'accordion'};
const paymentElement = elements.create('payment', paymentElementOptions);
paymentElement.mount('#payment-element');
```
#### Manually list payment methods
To manually list payment methods, add each one to the `PaymentMethodTypes` attribute in `options` when you create the [Payment Element](https://docs.stripe.com/js/elements_object/create_without_intent).
```javascript
const options = {mode: 'payment',
amount: 1099,
currency: 'usd',
paymentMethodTypes: ['us_bank_account'],
// Fully customizable with appearance API.
appearance: {/*...*/},
};
// Set up Stripe.js and Elements to use in checkout formconst elements = stripe.elements(options);
// Create and mount the Payment Element
const paymentElementOptions = { layout: 'accordion'};
const paymentElement = elements.create('payment', paymentElementOptions);
paymentElement.mount('#payment-element');
```
#### React
### Set up Stripe.js
Install React Stripe.js and the Stripe.js loader from the [npm public registry](https://www.npmjs.com/package/@stripe/react-stripe-js).
```bash
npm install --save @stripe/react-stripe-js @stripe/stripe-js
```
### Add and configure the Elements provider to your checkout page
To use the Payment Element component, wrap your checkout page component in an [Elements provider](https://docs.stripe.com/sdks/stripejs-react.md#elements-provider). Call `loadStripe` with your publishable key, and pass the returned `Promise` to the `Elements` provider.
You can manage payment methods from the Dashboard or manually list the payment methods you want to be available when you create the [Payment Element](https://docs.stripe.com/js/elements_object/create_without_intent).
#### Manage payment methods from the Dashboard
```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import {Elements} from '@stripe/react-stripe-js';
import {loadStripe} from '@stripe/stripe-js';
import CheckoutForm from './CheckoutForm';
// Make sure to call `loadStripe` outside of a component's render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe('<>');
function App() {
const options = {mode: 'payment',
amount: 1099,
currency: 'usd',
// Fully customizable with appearance API.
appearance: {/*...*/},
};
return (
);
};
ReactDOM.render(, document.getElementById('root'));
```
#### Manually list payment methods
To manually list payment methods, add each one to the `payment_method_types` attribute when you create the [Payment Element](https://docs.stripe.com/js/elements_object/create_without_intent).
```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import {Elements} from '@stripe/react-stripe-js';
import {loadStripe} from '@stripe/stripe-js';
import CheckoutForm from './CheckoutForm';
// Make sure to call `loadStripe` outside of a component's render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe('<>');
function App() {
const options = {mode: 'payment',
amount: 1099,
currency: 'usd',
paymentMethodTypes: ['us_bank_account'],
// Fully customizable with appearance API.
appearance: {/*...*/},
};
return (
);
};
ReactDOM.render(, document.getElementById('root'));
```
### Add the Payment Element component
Use the `PaymentElement` component to build your form.
```jsx
import React from 'react';
import {PaymentElement} from '@stripe/react-stripe-js';
const CheckoutForm = () => {
return (
);
};
export default CheckoutForm;
```
You can customise the Payment Element to match the design of your site by passing the [appearance object](https://docs.stripe.com/elements/appearance-api.md) into `options` when creating the `Elements` provider.
### Collect addresses
By default, the Payment Element only collects the necessary billing address details. Some behaviour, such as [calculating tax](https://docs.stripe.com/api/tax/calculations/create.md) or entering shipping details, requires your customer’s full address. You can:
- Use the [Address Element](https://docs.stripe.com/elements/address-element.md) to take advantage of autocomplete and localisation features to collect your customer’s full address. This helps ensure the most accurate tax calculation.
- Collect address details using your own custom form.
## Verify bank accounts
#### Item 1
For information on Financial Connections fees, see [pricing details](https://stripe.com/financial-connections#pricing).
Bank account verification uses automatic verification to collect payment information through [Financial Connections](https://docs.stripe.com/financial-connections.md) by default. No changes are required to use it. You can change your [verification_method](https://docs.stripe.com/js/elements_object/create_without_intent#stripe_elements_no_intent-options-paymentMethodOptions-us_bank_account-verification_method) to `instant` to make all users verify their bank accounts with Financial Connections. To learn how to configure Financial Connections and access additional account data, such as checking an account’s balance before initiating a payment, see the [Financial Connections](https://docs.stripe.com/financial-connections.md).
#### Automatic (default)
By default, collecting bank account payment information uses [Financial Connections](https://docs.stripe.com/financial-connections.md) to instantly verify your customer’s account. If instant verification isn’t possible, it falls back to manual account number entry and microdeposit verification.
> To expand access to additional data after a customer authenticates their account, the customer needs to re-link their account with expanded permissions. This authentication occurs one time per customer, and the permission it grants is reusable.
**Microdeposit verification**Not all customers can verify their bank account instantly. This section only applies if your customer has elected to opt out of the instant verification flow in the previous step. In these cases, Stripe sends 1-2 microdeposits to a customer’s bank account for verification instead. These deposits take 1-2 business days to appear on the customer’s online statement.
There are two types of microdeposits:
- **Descriptor code** (default): Stripe sends a 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 code 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.
**Microdeposit verification failure**When a bank account is pending verification with microdeposits, verification can fail in several ways:
- The microdeposits fail to arrive in the customer’s bank account (this usually indicates a closed or unavailable bank account or incorrect bank account number).
- The customer exceeds the limit of verification attempts for the account. Exceeding this limit means the bank account can no longer be verified or reused.
- The customer didn’t complete verification within 10 days.
#### Instant
You can enforce all of your customers to verify their bank accounts with Financial Connections by using instant verification.
To use instant verification, set the [verification_method](https://docs.stripe.com/js/elements_object/create_without_intent#stripe_elements_no_intent-options-paymentMethodOptions-us_bank_account-verification_method) parameter to `instant` when you create a [Payment Element](https://docs.stripe.com/js/elements_object/create_without_intent).
#### HTML + JS
```js
const options = {
mode: 'payment',
amount: 1099,
currency: 'usd',paymentMethodOptions: {
us_bank_account: {
verification_method: "instant"
}
}
appearance: {/*...*/},
};
// Set up Stripe.js and Elements to use in checkout form
const elements = stripe.elements(options);
// Create and mount the Payment Element
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
```
#### React
```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import {Elements} from '@stripe/react-stripe-js';
import {loadStripe} from '@stripe/stripe-js';
import CheckoutForm from './CheckoutForm';
// Make sure to call `loadStripe` outside of a component's render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe('<>');
function App() {
const options = {
mode: 'payment',
amount: 1099,
currency: 'usd',paymentMethodOptions: {
us_bank_account: {
verification_method: "instant"
}
}
// Fully customizable with appearance API.
appearance: {/*...*/},
};
return (
);
};
ReactDOM.render(, document.getElementById('root'));
```
### Add the Payment Element component
Use the `PaymentElement` component to build your form.
```jsx
import React from 'react';
import {PaymentElement} from '@stripe/react-stripe-js';
const CheckoutForm = () => {
return (
);
};
export default CheckoutForm;
```
#### Item 2
For information on Financial Connections fees, see [pricing details](https://stripe.com/financial-connections#pricing).
Bank account verification uses automatic verification to collect payment information through [Financial Connections](https://docs.stripe.com/financial-connections.md) by default. No changes are required to use it. You can change your [verification_method](https://docs.stripe.com/js/elements_object/create_without_intent#stripe_elements_no_intent-options-paymentMethodOptions-us_bank_account-verification_method) to `instant` to make all users verify their bank accounts with Financial Connections. To learn how to configure Financial Connections and access additional account data, such as checking an account’s balance before initiating a payment, see the [Financial Connections](https://docs.stripe.com/financial-connections.md).
#### Automatic (default)
By default, collecting bank account payment information uses [Financial Connections](https://docs.stripe.com/financial-connections.md) to instantly verify your customer’s account. If instant verification isn’t possible, it falls back to manual account number entry and microdeposit verification.
> To expand access to additional data after a customer authenticates their account, the customer needs to re-link their account with expanded permissions. This authentication occurs one time per customer, and the permission it grants is reusable.
**Microdeposit verification**Not all customers can verify their bank account instantly. This section only applies if your customer has elected to opt out of the instant verification flow in the previous step. In these cases, Stripe sends 1-2 microdeposits to a customer’s bank account for verification instead. These deposits take 1-2 business days to appear on the customer’s online statement.
There are two types of microdeposits:
- **Descriptor code** (default): Stripe sends a 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 code 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.
**Microdeposit verification failure**When a bank account is pending verification with microdeposits, verification can fail in several ways:
- The microdeposits fail to arrive in the customer’s bank account (this usually indicates a closed or unavailable bank account or incorrect bank account number).
- The customer exceeds the limit of verification attempts for the account. Exceeding this limit means the bank account can no longer be verified or reused.
- The customer didn’t complete verification within 10 days.
#### Instant
You can enforce all of your customers to verify their bank accounts with Financial Connections by using instant verification.
To use instant verification, set the [verification_method](https://docs.stripe.com/js/elements_object/create_without_intent#stripe_elements_no_intent-options-paymentMethodOptions-us_bank_account-verification_method) parameter to `instant` when you create a [Payment Element](https://docs.stripe.com/js/elements_object/create_without_intent).
#### HTML + JS
```js
const options = {
mode: 'payment',
amount: 1099,
payment_method_types: ["card", "us_bank_account"],
currency: 'usd',paymentMethodOptions: {
us_bank_account: {
verification_method: "instant"
}
}
appearance: {/*...*/},
};
// Set up Stripe.js and Elements to use in checkout form
const elements = stripe.elements(options);
// Create and mount the Payment Element
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
```
#### React
```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import {Elements} from '@stripe/react-stripe-js';
import {loadStripe} from '@stripe/stripe-js';
import CheckoutForm from './CheckoutForm';
// Make sure to call `loadStripe` outside of a component's render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe('<>');
function App() {
const options = {
mode: 'payment',
amount: 1099,
currency: 'usd',
payment_method_types: `us_bank_account`,paymentMethodOptions: {
us_bank_account: {
verification_method: "instant"
}
}
// Fully customizable with appearance API.
appearance: {/*...*/},
};
return (
);
};
ReactDOM.render(, document.getElementById('root'));
```
### Add the Payment Element component
Use the `PaymentElement` component to build your form.
```jsx
import React from 'react';
import {PaymentElement} from '@stripe/react-stripe-js';
const CheckoutForm = () => {
return (
);
};
export default CheckoutForm;
```
## Create a PaymentIntent [Server-side]
> #### Run custom business logic immediately before payment confirmation
>
> Navigate to [step 5](https://docs.stripe.com/payments/finalize-payments-on-the-server.md?platform=web&type=payment#submit-payment) in the finalise payments guide to run your custom business logic immediately before payment confirmation. Otherwise, follow the steps below for a simpler integration, which uses `stripe.confirmPayment` on the client to both confirm the payment and handle any next actions.
#### Control payment methods from the Dashboard
When the customer submits your payment form, use a *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) to facilitate the confirmation and payment process. Create a PaymentIntent on your server with an `amount` and `currency`. [Enable the payment method](https://dashboard.stripe.com/settings/payment_methods) in your Dashboard. With [dynamic payment methods](https://docs.stripe.com/payments/payment-methods/dynamic-payment-methods.md), Stripe displays eligible payment methods to your customers automatically. To prevent malicious customers from choosing their own prices, always decide how much to charge on the server-side (a trusted environment) and not the client.
A `PaymentIntent` includes 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)). Return this value to your client for Stripe.js to use to securely complete the payment process.
#### Accounts v2
#### Ruby
```ruby
require 'stripe'
# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
client = Stripe::StripeClient.new('<>')
post '/create-intent' do
intent = client.v1.payment_intents.create({
# To allow saving and retrieving payment methods, provide the customer's Account ID.
customer_account: "{{CUSTOMER_ACCOUNT_ID}}",
amount: 1099,
currency: 'usd',
automatic_payment_methods: { enabled: true },
})
{client_secret: intent.client_secret}.to_json
end
```
#### Customers v1
#### Ruby
```ruby
require 'stripe'
# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
client = Stripe::StripeClient.new('<>')
post '/create-intent' do
intent = client.v1.payment_intents.create({
# To allow saving and retrieving payment methods, provide the Customer ID.
customer: customer.id,
amount: 1099,
currency: 'usd',
automatic_payment_methods: { enabled: true },
})
{client_secret: intent.client_secret}.to_json
end
```
#### List payment methods manually
When the customer submits your payment form, use a *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) to facilitate the confirmation and payment process. Create a PaymentIntent on your server with an `amount`, `currency`, and one or more payment methods using `payment_method_types`. To prevent malicious customers from choosing their own prices, always decide how much to charge on the server-side (a trusted environment) and not the client.
Included on a 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)). Return this value to your client for Stripe.js to use to securely complete the payment process.
#### Accounts v2
#### Ruby
```ruby
require 'stripe'
# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
client = Stripe::StripeClient.new('<>')
post '/create-intent' do
intent = client.v1.payment_intents.create({
# To allow saving and retrieving payment methods, provide the customer's Account ID.
customer_account: customer_account.id,
amount: 1099,
currency: 'usd',
payment_method_types: ['us_bank_account'],
})
{client_secret: intent.client_secret}.to_json
end
```
#### Customers v1
#### Ruby
```ruby
require 'stripe'
# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
client = Stripe::StripeClient.new('<>')
post '/create-intent' do
intent = client.v1.payment_intents.create({
# To allow saving and retrieving payment methods, provide the Customer ID.
customer: customer.id,
amount: 1099,
currency: 'usd',
payment_method_types: ['us_bank_account'],
})
{client_secret: intent.client_secret}.to_json
end
```
## Submit the payment to Stripe [Client-side]
Use [stripe.confirmPayment](https://docs.stripe.com/js/payment_intents/confirm_payment) to complete the payment using details from the Payment Element.
Provide a [return_url](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-return_url) to this function to indicate where Stripe redirects the user after they complete the payment. Your user might be initially redirected to an intermediate site, such as a bank authorisation page, before being redirected to the `return_url`. Card payments immediately redirect to the `return_url` when a payment is successful.
If you don’t want to redirect for card payments after payment completion, you can set [redirect](https://docs.stripe.com/js/payment_intents/confirm_payment#confirm_payment_intent-options-redirect) to `if_required`. This only redirects customers who check out with redirect-based payment methods.
#### HTML + JS
```javascript
const form = document.getElementById('payment-form');
const submitBtn = document.getElementById('submit');
const handleError = (error) => {
const messageContainer = document.querySelector('#error-message');
messageContainer.textContent = error.message;
submitBtn.disabled = false;
}
form.addEventListener('submit', async (event) => {
// We don't want to let default form submission happen here,
// which would refresh the page.
event.preventDefault();
// Prevent multiple form submissions
if (submitBtn.disabled) {
return;
}
// Disable form submission while loading
submitBtn.disabled = true;
// Trigger form validation and wallet collection
const {error: submitError} = await elements.submit();
if (submitError) {
handleError(submitError);
return;
}
// Create the PaymentIntent and obtain clientSecret
const res = await fetch("/create-intent", {
method: "POST",
});
const {client_secret: clientSecret} = await res.json();
// Confirm the PaymentIntent using the details collected by the Payment Element
const {error} = await stripe.confirmPayment({
elements,
clientSecret,
confirmParams: {
return_url: 'https://example.com/order/123/complete',
},
});
if (error) {
// This point is only reached if there's an immediate error when
// confirming the payment. Show the error to your customer (for example, payment details incomplete)
handleError(error);
} else {
// Your customer is redirected to your `return_url`. For some payment
// methods like iDEAL, your customer is redirected to an intermediate
// site first to authorize the payment, then redirected to the `return_url`.
}
});
```
#### React
```jsx
import React, {useState} from 'react';
import {useStripe, useElements, PaymentElement} from '@stripe/react-stripe-js';
export default function CheckoutForm() {
const stripe = useStripe();
const elements = useElements();
const [errorMessage, setErrorMessage] = useState();
const [loading, setLoading] = useState(false);
const handleError = (error) => {
setLoading(false);
setErrorMessage(error.message);
}
const handleSubmit = async (event) => {
// We don't want to let default form submission happen here,
// which would refresh the page.
event.preventDefault();
if (!stripe) {
// Stripe.js hasn't yet loaded.
// Make sure to disable form submission until Stripe.js has loaded.
return;
}
setLoading(true);
// Trigger form validation and wallet collection
const {error: submitError} = await elements.submit();
if (submitError) {
handleError(submitError);
return;
}
// Create the PaymentIntent and obtain clientSecret
const res = await fetch("/create-intent", {
method: "POST",
});
const {client_secret: clientSecret} = await res.json();
// Confirm the PaymentIntent using the details collected by the Payment Element
const {error} = await stripe.confirmPayment({
elements,
clientSecret,
confirmParams: {
return_url: 'https://example.com/order/123/complete',
},
});
if (error) {
// This point is only reached if there's an immediate error when
// confirming the payment. Show the error to your customer (for example, payment details incomplete)
handleError(error);
} else {
// Your customer is redirected to your `return_url`. For some payment
// methods like iDEAL, your customer is redirected to an intermediate
// site first to authorize the payment, then redirected to the `return_url`.
}
};
return (
);
}
```
## Optional: Handle post-payment events
Stripe sends a [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) event when the payment completes. Use the Dashboard, a custom *webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests), or a partner solution to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.
Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes, and malicious clients could manipulate the response. Setting up your integration to listen for asynchronous events also helps you accept more payment methods in the future. Learn about the [differences between all supported payment methods](https://stripe.com/payments/payment-methods-guide).
- **Handle events manually in the Dashboard**
Use the Dashboard to [view your payments](https://dashboard.stripe.com/payments), send email receipts, handle payouts, or retry failed payments.
- **Build a custom webhook**
[Build a custom webhook](https://docs.stripe.com/webhooks/handling-payment-events.md#build-your-own-webhook) handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.
- **Integrate a prebuilt app**
Handle common business events, such as [automation](https://stripe.partners/?f_category=automation) or [marketing and sales](https://stripe.partners/?f_category=marketing-and-sales), by integrating a partner application.
## 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 are routed 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 [tokenised 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 behaviour
Test transactions settle instantly and are added to your available test balance. This behaviour differs from live mode, 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.
## Error codes
The following table details common error codes and recommended actions:
| Error code | Recommended action |
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment_intent_invalid_currency` | Enter a supported currency. |
| `missing_required_parameter` | Check the error message for more information about the required parameter. |
| `payment_intent_payment_attempt_failed` | This code can appear in the [last_payment_error.code](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-last_payment_error-code) field of a PaymentIntent. Check the error message for a detailed failure reason and suggestion on error handling. |
| `payment_intent_authentication_failure` | This code can appear in the [last_payment_error.code](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-last_payment_error-code) field of a PaymentIntent. Check the error message for a detailed failure reason and suggestion on error handling. This error occurs when you manually trigger a failure when testing your integration. |
| `payment_intent_redirect_confirmation_without_return_url` | Provide a `return_url` when confirming a PaymentIntent. |
## Optional: Access data on a Financial Connections bank account
For more information on Financial Connections fees, see [pricing details](https://stripe.com/financial-connections#pricing).
You can only access [Financial Connections](https://docs.stripe.com/financial-connections.md) data if you request additional [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) when you create your Payment Intent.
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 Payment Intent and expand the `payment_intent.payment_method` attribute:
```curl
curl -G https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}} \
-u "<>:" \
-d "expand[]=payment_method"
```
```json
{
"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"
}
}
// ...
}
}
```
## 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 authorisation 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.
2. Confirm authorization from your customer again.
To confirm authorisation 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 authorisation 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/payment_intents/object.md#payment_intent_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. You can [cancel a PaymentIntent](https://docs.stripe.com/api/payment_intents/cancel.md) created with a target date up to three business days before the configured date.
Target dates that meet one of the following criteria delay the debit until 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/payment_intents/object.md#payment_intent_object-payment_method_options-us_bank_account-verification_method) to `microdeposits` when using a [target date](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_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.