Save details for future payments with ACH Direct Debit
Learn how to save payment method details for future ACH Direct Debit payments.
You can use the Setup Intents API to collect payment method details in advance, with the final amount or payment date determined later. This is useful for:
- Saving payment methods to a wallet to streamline future purchases
- Collecting surcharges after fulfilling a service
- Starting a free trial for a subscription
Note
ACH Direct Debit is a delayed 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.
Server-side
This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:
Client-side
The React Native SDK is open source and fully documented. Internally, it uses the native iOS and Android SDKs. To install Stripe’s React Native SDK, run one of the following commands in your project’s directory (depending on which package manager you use):
Next, install some other necessary dependencies:
- For iOS, go to the ios directory and run
pod install
to ensure that you also install the required native dependencies. - For Android, there are no more dependencies to install.
Note
We recommend following the official TypeScript guide to add TypeScript support.
Stripe initialisation
To initialise Stripe in your React Native app, either wrap your payment screen with the StripeProvider
component, or use the initStripe
initialisation method. Only the API publishable key in publishableKey
is required. The following example shows how to initialise Stripe using the StripeProvider
component.
Create a Customer 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 optimisation.
A SetupIntent is an object that represents your intent to set up a customer’s payment method for future payments. The SetupIntent
tracks the steps of this set-up process.
Create a SetupIntent on your server with payment_method_types set to us_
and specify the Customer’s id.
Server-side
Create a SetupIntent on your server with payment_method_types set to us_
and specify the Customer’s id.
By default, collecting bank account payment information uses Financial Connections to instantly verify your customer’s account, with a fallback option of manual account number entry and microdeposit verification. See the Financial Connections docs to learn how to configure Financial Connections and access additional account data to optimise your ACH integration. For example, you can use Financial Connections to check an account’s balance before initiating the ACH payment.
Note
To expand access to additional data after a customer authenticates their account, they must re-link their account with expanded permissions.
Client-side
A SetupIntent includes a client secret. You can use the client secret in your React Native app to securely complete the payment process instead of passing back the entire SetupIntent object. In your app, request a SetupIntent from your server and store its client secret.
Rather than sending the entire SetupIntent object to the client, use its client secret from the previous step. This is different from your API keys that authenticate Stripe API requests.
Handle the client secret carefully because it can complete the charge. Don’t log it, embed it in URLs, or expose it to anyone but the customer
Use collectBankAccountForSetup to collect bank account details, create a PaymentMethod, and attach that PaymentMethod to the SetupIntent. You must include the account holder’s name in the billingDetails
parameter to create an ACH Direct Debit PaymentMethod.
This loads a modal UI that handles bank account details collection and verification. When it completes, the PaymentMethod is automatically attached to the SetupIntent.
When a customer exits your app (for example to authenticate in Safari or their banking app), provide a way for them to automatically return to your app. Many payment method types require a return URL. If you don’t provide one, we can’t present payment methods that require a return URL to your users, even if you’ve enabled them.
To provide a return URL:
- Register a custom URL. Universal links aren’t supported.
- Configure your custom URL.
- Set up your root component to forward the URL to the Stripe SDK as shown below.
Note
If you’re using Expo, set your scheme in the app.
file.
For more information on native URL schemes, refer to the Android and iOS docs.
You can only access Financial Connections data if you request additional data permissions when you create your SetupIntent.
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 SetupIntent and expand the payment_
attribute:
If you have access to balances data, we recommend checking balances before initiating payments with the collected Payment Method.
Learn more about using additional account data to optimise your ACH integration with Financial Connections.
Before you can complete the SetupIntent and save the payment method details for the customer, you must obtain authorization for payment by displaying mandate terms for the customer to accept.
To comply with Nacha rules, you must obtain authorisation from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see Mandates.
When the customer accepts the mandate terms, you must confirm the SetupIntent. Use confirmSetup
to confirm the intent.
If successful, Stripe returns a SetupIntent object, with one of the following possible statuses:
Status | Description | Next Steps |
---|---|---|
Succeeded | The bank account has been instantly verified or verification wasn’t necessary. | No action needed |
RequiresAction | Bank account verification requires further action. | Step 6: Verifying bank accounts with microdeposits |
After successfully confirming the SetupIntent, 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 if you prefer.
Not all customers can verify their bank account instantly. In these cases, Stripe sends a microdeposit to the bank account. This deposit might take up to 1-2 business days to appear on the customer’s online statement. This deposit takes one of two shapes:
- Descriptor code. Stripe sends a single, 0.01 USD microdeposit to the customer’s bank account with a unique, 6-digit
descriptorCode
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.
If the result of the confirmSetup
method call in the previous step is a SetupIntent with a requiresAction
status, the SetupIntent contains a next_
field that contains some useful information for completing the verification.
If you supplied a billing email, Stripe uses this email to notify your customer when we expect the deposits 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.
Warning
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.
When you know the payment is in the requiresAction
state and the nextAction
is of type verifyWithMicrodeposits
, you can complete verification of a bank account in two ways:
- Verify the deposits directly in your app by calling
verifyMicrodepositsForSetup
after collecting either the descriptor code or amounts.
- Use the Stripe-hosted verification page that we automatically provide for you. To do this, use the
nextAction[redirectUrl]
URL in thenextAction
object (see above) to direct your customer to complete the verification process.
When the bank account is successfully verified, Stripe returns the SetupIntent object with a status
of Succeeded
, and sends a setup_intent.succeeded webhook event.
Verification can fail for several reasons. The failure might happen synchronously as a direct error response, or asynchronously through a setup_intent.setup_failed webhook event (shown in the following examples).
Error Code | Synchronous or Asynchronous | Message | Status change |
---|---|---|---|
payment_ | Synchronously, or asynchronously through webhook event | Microdeposits failed. Please check the account, institution and transit numbers provided | status is requires_ , and last_ is set. |
payment_ | Synchronously | The amounts provided don’t match the amounts that we sent to the bank account. You have {attempts_remaining} verification attempts remaining. | Unchanged |
payment_ | Synchronously, or asynchronously through webhook event | Exceeded the number of allowed verification attempts | status is requires_ , and last_ is set. |
payment_ | Asynchronously through a webhook event | Microdeposit timeout. The customer hasn’t verified their bank account within the required 10 day period. | status is requires_ , and last_ is set. |
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 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.
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 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 to settle in your available balance.
When the SetupIntent succeeds, it creates a new PaymentMethod attached to a Customer. You can use these to initiate future payments without having to prompt the customer for their bank account a second time.
By default, setting up a US bank account payment method allows your customers to use instant bank account verification or microdeposits. You can optionally require instant bank account verification only, using the verification_method parameter when you create the SetupIntent.
This ensures that you don’t need to handle microdeposit verification. However, if instant verification fails, the SetupIntent’s status is requires_
, indicating a failure to instantly verify a bank account for your customer.
By default, setting up a US bank account payment method lets your customers use instant bank account verification or microdeposits. You can optionally require microdeposit verification using the verification_method parameter when you create the SetupIntent.
Caution
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.
You must then collect your customer’s bank account with your own UI and call confirmSetupIntent with those details to complete the SetupIntent.