Accept an ACH Direct Debit payment
Build a custom payment form or use Stripe Checkout to accept payments with ACH Direct Debit.
Use the Payment Element to embed a custom Stripe payment form in your website or application and offer payment methods to customers. For advanced configurations and customizations, refer to the Accept a Payment 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.
- Choose how you verify bank accounts.
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 StripeServer-side
To get started, create a Stripe account.
Use our official libraries for access to the Stripe API from your application:
Collect payment detailsClient-side
Use the Payment Element to collect payment details on the client. The Payment Element is a prebuilt 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 when you’re ready to accept live payments.
You can customize the Payment Element to match the design of your site by passing the appearance object into options
when creating the Elements
provider.
Collect addresses
By default, the Payment Element only collects the necessary billing address details. To collect a customer’s full billing address (to calculate the tax for digital goods and services, for example) or shipping address, use the Address Element.
Create a PaymentIntentServer-side
Run custom business logic immediately before payment confirmation
Navigate to step 5 in the finalize payments guide to run your custom business logic immediately before payment confirmation. Otherwise, follow the steps below for a simpler integration, which uses stripe.
on the client to both confirm the payment and handle any next actions.
Submit the payment to StripeClient-side
Use stripe.confirmPayment to complete the payment using details from the Payment Element.
Provide a 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 authorization page, before being redirected to the return_
. Card payments immediately redirect to the return_
when a payment is successful.
If you don’t want to redirect for card payments after payment completion, you can set redirect to if_
. This only redirects customers that check out with redirect-based payment methods.
OptionalHandle post-payment events
Stripe sends a payment_intent.succeeded event when the payment completes. Use the Dashboard, a custom webhook, 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.
Handle events manually in the Dashboard
Use the Dashboard to View your test payments in the Dashboard, send email receipts, handle payouts, or retry failed payments.
Build a custom webhook
Build a custom 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 or marketing and sales, by integrating a partner application.
Test your integration
Learn how to test scenarios with instant verifications using Financial Connections.
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.
If your domain is {domain} and your username is {username}, use the 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.
Common mistake
You need to activate your Stripe account 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_ | 110000000 | The payment succeeds. |
000111111113 | pm_ | 110000000 | The payment fails because the account is closed. |
000000004954 | pm_ | 110000000 | The payment is blocked by Radar due to a high risk of fraud. |
000111111116 | pm_ | 110000000 | The payment fails because no account is found. |
000222222227 | pm_ | 110000000 | The payment fails due to insufficient funds. |
000333333335 | pm_ | 110000000 | The payment fails because debits aren’t authorized. |
000444444440 | pm_ | 110000000 | The payment fails due to invalid currency. |
000666666661 | pm_ | 110000000 | The payment fails to send microdeposits. |
000555555559 | pm_ | 110000000 | The payment triggers a dispute. |
000000000009 | pm_ | 110000000 | The payment stays in processing indefinitely. Useful for testing PaymentIntent cancellation. |
000777777771 | pm_ | 110000000 | The payment fails due to payment amount causing the account to exceed its weekly payment volume limit. |
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 to settle in your available balance.
Error codes 
The following table details common error codes and recommended actions:
Error code | Recommended action |
---|---|
payment_ | Enter a supported currency. |
missing_ | Check the error message for more information about the required parameter. |
payment_ | This code can appear in the last_payment_error.code field of a PaymentIntent. Check the error message for a detailed failure reason and suggestion on error handling. |
payment_ | This code can appear in the 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_ | Provide a return_ when confirming a PaymentIntent. |
OptionalAccess data on a Financial Connections bank account
You can only access Financial Connections data if you request additional data permissions when you create your Payment Intent.
After your customer successfully completes the Stripe Financial Connections authentication flow, the us_
PaymentMethod returned includes a financial_connections_account ID that points to a Financial Connections Account. Use this ID to access account data.
Common mistake
Bank accounts that your customers link through manual entry and microdeposits don’t have a financial_
ID on the payment method.
To determine the Financial Connections account ID, retrieve the Payment Intent and expand the payment_
attribute:
{ "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" } } // ... } }
OptionalResolve disputesServer-side
Customers can generally dispute an ACH Direct Debit payment 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 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:
{ "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_
state. To continue with the payment, you must:
- Resolve the dispute with the customer to ensure future payments will not be disputed.
- Confirm authorization from your customer again.
To confirm authorization for the payment, you can collect mandate acknowledgement from your customer online with Stripe.js or confirm authorization with your customer offline using the Stripe API.
Caution
If a customer disputes more than one payment from the same bank account, Stripe blocks their bank account. Contact Stripe Support for further resolution.
OptionalPayment 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.
Charge State | Payment Reference Value |
---|---|
Pending | Unavailable |
Failed | Unavailable |
Succeeded | Available (for example, 091000015001234) |
In addition, when you receive the charge.
webhook, view the content of payment_
to locate the payment_reference.
The following example event shows the rendering of a successful ACH payment with a payment reference number.
{ "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_
to locate the refund 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.
{ "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" } } // ... } } }
OptionalConfigure customer debit dateServer-side
You can control the date that Stripe debits a customer’s bank account using the 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 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.
Caution
You can’t set the verification method to microdeposits
when using a target date, as the verification process could take longer than the target date, causing payments to arrive later than expected.