Migrating ACH Direct Debit from Charges to newer APIs
Learn why and how to migrate from the Charges API.
Stripe is removing support for ACH Direct Debit using legacy integrations.
If you create legacy ACH Direct Debit payments, you must migrate to the Payment Intents API or Checkout Sessions API.
Feature comparison
Stripe’s new APIs offer features that aren’t available in legacy integrations:
| Feature | Legacy Integrations | Payment Intents API or Checkout Sessions API |
|---|---|---|
| Checkout support | No | Yes |
| Payment Element support | No | Yes |
| Dynamic payment method support | No | Yes |
| Settlement speed | T+6 | T+4 (T+2 when using faster settlement) |
| Instant bank account verification | Only available through custom, third-party integrations | Instant verification with Financial Connections |
| Fraud prevention | No |
|
| Supported countries | US | US, EU and UK |
Compare the Checkout Sessions and Payment Intents APIs
Stripe offers two new APIs to accept ACH Direct Debits payments: Payment Intents and Checkout Sessions APIs.
Checkout Sessions API: Supports common checkout workflows with built-in features that remove the need for custom code and is recommended for most developers.
Payment Intents API: A lower-level API for building your own checkout flow. It requires significantly more code and ongoing maintenance. We recommend Checkout Sessions for most integrations.
Learn more about the differences, and how to evaluate which is right for you.
Build an ACH Direct Debit integration
To build an ACH Direct Debit integration on Payment Intents or Checkout Sessions:
Enable ACH Direct Debit in your Payment methods settings.
To collect and use new payment methods, integrate with ACH on Payment Intents or Checkout Sessions.
For bank accounts previously collected using the Tokens API, you can continue to use saved
BankAccountobjects asPaymentMethodobjects with the Payment Intents API. For details, see Migrate existing bank accounts.Gradually migrate all payments using existing bank accounts to the Payment Intents or Checkout Sessions API.
Remove your legacy integration.
Behavioral differences
Some features that exist in both APIs work differently. If you rely on any of the following behaviors, update your integration accordingly.
| Behavior | Legacy Integrations | Payment Intents or Checkout Sessions API |
|---|---|---|
| Mandates | Not enforced by the API. |
|
| Balance transactions |
|
|
| Balance type | Funds settle in source_. |
|
| Microdeposits |
|
|
| Emails | Stripe doesn’t send automatic emails. |
|
Webhook differences
If you previously listened to Charge events, you might need to update your integration to listen to new event types. The following table shows how webhook events differ.
| Old webhook | New webhook on Checkout | New webhook on Payment Intents | Special instructions |
|---|---|---|---|
charge. | payment_ | payment_ | In legacy integrations, charge. includes balance transaction. In new integrations, the balance transaction isn’t available until the charge. event. |
charge. | charge. | charge. | Sent when the payment is submitted to the banking partner. Includes the balance transaction. |
charge. | checkout. | payment_ | The charge. webhook is also sent, so you don’t have to update your integration to listen to the new webhook. |
charge. | Not applicable. The customer can re-attempt the payment on the same Checkout Session until it expires, at which point you receive a checkout. event. | payment_ | The charge. webhook is also sent, so you don’t have to update your integration to listen to the new webhook. |
charge. | charge. | charge. | Not applicable |
| Not applicable | mandate. | mandate. | Sent when a mandate becomes inactive. |
Identify Legacy ACH Payments
On a Charge object, the payment_ property is ach_ for the legacy integration and us_ for the newer integration.
A legacy ACH payment is created when a legacy BankAccount is the payment source. This happens when:
- You call the Create Charge API.
- A Subscription or Invoice charges a customer whose
default_is a legacy BankAccount, and nosource default_is set on the customer, subscription, or invoice.payment_ method - You call the Create PaymentIntent API with
sourceset to a legacy BankAccount.