Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Revenue
Platforms and marketplaces
Money management
Developer resources
Overview
About Stripe payments
Upgrade your integration
Payments analytics
Online payments
OverviewFind your use caseManaged Payments
Use Payment Links
Build a checkout page
Build an advanced integration
Build an in-app integration
Payment methods
Add payment methods
    Overview
    Payment method integration options
    Manage default payment methods in the Dashboard
    Payment method types
    Cards
    Pay with Stripe balance
    Crypto
    Bank debits
      ACH Direct Debit
        Accept a payment
        Save bank details
        Migrating from the Charges API
        Migrating from another processor
        Blocked bank accounts
        SEC codes
      Bacs Direct Debit
      Pre-authorized debit in Canada
      Australia BECS Direct Debit
      New Zeland BECS Direct Debit
      SEPA Direct Debit
    Bank redirects
    Bank transfers
    Credit transfers (Sources)
    Buy now, pay later
    Real-time payments
    Vouchers
    Wallets
    Enable local payment methods by country
    Custom payment methods
Manage payment methods
Faster checkout with Link
Payment interfaces
Payment Links
Checkout
Web Elements
In-app Elements
Payment scenarios
Handle multiple currencies
Custom payment flows
Flexible acquiring
Orchestration
In-person payments
Terminal
Beyond payments
Incorporate your company
Crypto
Financial Connections
Climate
Understand fraud
Radar fraud protection
Manage disputes
Verify identities
HomePaymentsAdd payment methodsBank debitsACH Direct Debit

Accept an ACH Direct Debit payment

Build a custom payment form or use Stripe Checkout to accept payments with ACH Direct Debit.

Accepting ACH Direct Debit payments in your app consists of:

  • Creating an object to track a payment
  • Collecting payment method information with instant verifications enabled by Stripe Financial Connections
  • Submitting the payment to Stripe for processing
  • Verifying your customer’s bank account

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.

Stripe uses the payment object, the Payment Intent, to track and handle all the states of the payment until the payment completes.

The Stripe Android SDK is open source and fully documented.

To install the SDK, add stripe-android and financial-connections to the dependencies block of your app/build.gradle file:

build.gradle.kts
Kotlin
Groovy
No results
plugins { id("com.android.application") } android { ... } dependencies { // ... // Stripe Android SDK implementation("com.stripe:stripe-android:21.20.2") // Include the financial connections SDK to support US bank account as a payment method implementation("com.stripe:financial-connections:21.20.2") // Financial Connections SDK implementation("com.stripe:financial-connections:21.20.2") }

Note

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.

Configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API, such as in your Application subclass:

Kotlin
Java
No results
import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext,
"pk_test_TYooMQauvdEDq54NiTphI7jx"
) } }

Note

Use your test keys while you test and develop, and your live mode keys when you publish your app.

Create or retrieve a customer
Recommended
Server-side

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 optimization.

Command Line
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node
Go
.NET
No results
curl https://api.stripe.com/v1/customers \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -d email={{CUSTOMER_EMAIL}}

Create a PaymentIntent
Server-side
Client-side

A PaymentIntent is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage.

Server-side

First, create a PaymentIntent on your server and specify the amount to collect and the usd currency. If you already have an integration using the Payment Intents API, add us_bank_account to the list of payment method types for your PaymentIntent. Optionally, specify the id of the Customer.

If you want to reuse the payment method in the future, provide the setup_future_usage parameter with the value of off_session.

For more information on Financial Connections fees, see pricing details.

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 optimize 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.

Command Line
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node
Go
.NET
No results
curl https://api.stripe.com/v1/payment_intents \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -d amount=1099 \ -d currency=usd \ -d setup_future_usage=off_session \ -d customer={{CUSTOMER_ID}} \ -d "payment_method_types[]"=us_bank_account

Client-side

Included in the returned PaymentIntent is a client secret, which the client side can use to securely complete the payment process instead of passing the entire PaymentIntent object. On the client, request a PaymentIntent from your server and use the client secret for subsequent API calls.

Kotlin
Java
No results
import androidx.appcompat.app.AppCompatActivity class CheckoutActivity : AppCompatActivity() { private lateinit var paymentIntentClientSecret: String fun startCheckout() { // Request a PaymentIntent from your server and store its client secret } }

Collect payment method details
Client-side

ACH Direct Debit requires a customer name and (optional) email for the payment to succeed. In your app, collect the required billing details from the customer:

  • Their full name (first and last)
  • Their email address

Use CollectBankAccountConfiguration.USBankAccount to create the parameters required to call presentWithPaymentIntent.

Initialize a CollectBankAccountLauncher instance inside onCreate of your checkout Activity, passing a method to handle the result. Then proceed to call presentWithPaymentIntent to collect bank account details, create a PaymentMethod, and attach that PaymentMethod to the PaymentIntent.

Kotlin
Java
No results
import androidx.appcompat.app.AppCompatActivity class CheckoutActivity : AppCompatActivity() { private lateinit var paymentIntentClientSecret: String private lateinit var collectBankAccountLauncher CollectBankAccountLauncher override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Create collector collectBankAccountLauncher = CollectBankAccountLauncher.create( this ) { result: CollectBankAccountResult -> when (result) { is CollectBankAccountResult.Completed -> { val intent = result.response.intent if (intent.status === StripeIntent.Status.RequiresPaymentMethod) { // Customer canceled the Financial Connections modal. Present them with other // payment method type options. } else if (intent.status === StripeIntent.Status.RequiresConfirmation) { // We collected an account - possibly instantly verified, but possibly // manually-entered. Display payment method details and mandate text // to the customer and confirm the intent once they accept // the mandate. } } is CollectBankAccountResult.Cancelled -> { // handle cancellation } is CollectBankAccountResult.Failed -> { // handle error print("Error: ${result.error}") } } } } fun startCheckout() { // ... // Build params val configuration: CollectBankAccountConfiguration = CollectBankAccountConfiguration.USBankAccount( name, email ) // Calling this method will trigger the Financial Connections modal to be displayed collectBankAccountLauncher.presentWithPaymentIntent( publishableKey, paymentIntentClientSecret, collectParams ) } }

This loads a modal UI that handles bank account details collection and verification. When it completes, the PaymentMethod automatically attaches to the PaymentIntent.

OptionalCustomize the sheet
Client-side

OptionalAccess data on a Financial Connections bank account
Server-side

Collect mandate acknowledgement and submit the payment
Client-side

Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.

To be compliant with Nacha rules, you must obtain authorization 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 Payment. Use confirm to complete the payment when the customer submits the form.

Kotlin
Java
No results
class CheckoutActivity : AppCompatActivity() { // ... private lateinit var paymentIntentClientSecret: String private val paymentLauncher: PaymentLauncher by lazy { val paymentConfiguration = PaymentConfiguration.getInstance(applicationContext) PaymentLauncher.Companion.create( this, paymentConfiguration.publishableKey, paymentConfiguration.stripeAccountId, ::onPaymentResult ) } private fun startCheckout() { // ... val confirmParams = ConfirmPaymentIntentParams.create( clientSecret = paymentIntentClientSecret, paymentMethodType = PaymentMethod.Type.USBankAccount ) paymentLauncher.confirm(confirmParams) } private fun onPaymentResult(paymentResult: PaymentResult) { when (paymentResult) { is PaymentResult.Completed -> { // show success UI } is PaymentResult.Canceled -> { // handle cancel flow } is PaymentResult.Failed -> { // handle failures // (for example, the customer might need to choose a new payment // method) } } } }

Note

confirm may take several seconds to complete. During that time, disable resubmittals of your form and show a waiting indicator (for example, a spinner). If you receive an error, show it to the customer, re-enable the form, and hide the waiting indicator.

If successful, Stripe returns a PaymentIntent object, with one of the following possible statuses:

StatusDescriptionNext Steps
requires_actionFurther action is needed to complete bank account verification.Step 5: Verifying bank accounts with microdeposits
processingThe bank account was instantly verified or verification isn’t necessary.Step 6: Confirm the PaymentIntent succeeded

After successfully confirming the Payment, 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.

Verify bank account with micro-deposits
Client-side

Not all customers can verify the bank account instantly. This step only applies if your customer has elected to opt out of the instant verification flow in the previous step.

In these cases, Stripe sends a descriptor_code microdeposit and might fall back to an amount microdeposit if any further issues arise with verifying the bank account. These deposits take 1-2 business days to appear on the customer’s online statement.

  • Descriptor code. Stripe sends a single, 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 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.

The result of the confirmPayment method call in the previous step is a PaymentIntent in the requires_action state. The PaymentIntent contains a next_action field that contains some useful information for completing the verification.

If you supplied a billing email, Stripe notifies your customer through this email when the deposits are expected 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.

Optional: Send custom email notifications

You can also send custom email notifications to your customer. After you set up custom emails, you need to specify how the customer responds to the verification email. To do so, choose one of the following:

  • Use the Stripe-hosted verification page. To do this, use the verify_with_microdeposits[hosted_verification_url] URL in the next_action object to direct your customer to complete the verification process.

  • If you prefer not to use the Stripe-hosted verification page, create a form in your app. Your customers then use this form to relay microdeposit amounts to you and verify the bank account using the Android SDK.

    • At minimum, set up the form to handle the descriptor code parameter, which is a 6-digit string for verification purposes.
    • Stripe also recommends that you set your form to handle the amounts parameter, as some banks your customers use may require it.

    Integrations only pass in the descriptor_code or amounts. To determine which one your integration uses, check the value for verify_with_microdeposits[microdeposit_type] in the next_action object.

Kotlin
Java
No results
// Use if you are using a descriptor code, do not use if you are using amounts fun verifyPaymentIntentWithMicrodeposits( clientSecret: String, descriptorCode: String, callback: ApiResultCallback<PaymentIntent> ) // Use if you are using amounts, do not use if you are using descriptor code fun verifyPaymentIntentWithMicrodeposits( clientSecret: String, firstAmount: Int, secondAmount: Int, callback: ApiResultCallback<PaymentIntent> )

When the bank account is successfully verified, Stripe returns the PaymentIntent object with a status of processing.

Verification can fail for several reasons. The failure happens synchronously as a direct error response.

{ "error": { "code": "payment_method_microdeposit_verification_amounts_mismatch", "message": "The amounts provided do not match the amounts that were sent to the bank account. You have {attempts_remaining} verification attempts remaining.", "type": "invalid_request_error" } }
Error CodeMessageStatus change
payment_method_microdeposit_failedMicrodeposits failed. Please check the account, institution and transit numbers provided.status is requires_payment_method, and last_payment_error is set.
payment_method_microdeposit_verification_amounts_mismatchThe amounts provided do not match the amounts that were sent to the bank account. You have {attempts_remaining} verification attempts remaining.Unchanged
payment_method_microdeposit_verification_attempts_exceededExceeded number of allowed verification attemptsstatus is requires_payment_method, and last_payment_error is set.

Confirm the PaymentIntent succeeded
Server-side

ACH Direct Debit is a delayed notification payment method. This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of processing. After the payment has succeeded, the PaymentIntent status is updated from processing to succeeded.

We recommend using webhooks to confirm the charge has succeeded and to notify the customer that the payment is complete. You can also view events on the Stripe Dashboard.

The following events are sent when the PaymentIntent status is updated:

EventDescriptionNext Step
payment_intent.processingThe customer’s payment was submitted to Stripe successfully.Wait for the initiated payment to succeed or fail.
payment_intent.succeededThe customer’s payment succeeded.Fulfill the goods or services that were purchased.
payment_intent.payment_failedThe customer’s payment was declined. This can also apply to a failed microdeposit verification.Contact the customer through email or push notification and request another payment method. If the webhook was sent due to a failed microdeposit verification, the user needs to enter in their bank account details again and a new set of microdeposits will be deposited in their account.

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 numberTokenRouting numberBehavior
000123456789pm_usBankAccount_success110000000The payment succeeds.
000111111113pm_usBankAccount_accountClosed110000000The payment fails because the account is closed.
000000004954pm_usBankAccount_riskLevelHighest110000000The payment is blocked by Radar due to a high risk of fraud.
000111111116pm_usBankAccount_noAccount110000000The payment fails because no account is found.
000222222227pm_usBankAccount_insufficientFunds110000000The payment fails due to insufficient funds.
000333333335pm_usBankAccount_debitNotAuthorized110000000The payment fails because debits aren’t authorized.
000444444440pm_usBankAccount_invalidCurrency110000000The payment fails due to invalid currency.
000666666661pm_usBankAccount_failMicrodeposits110000000The payment fails to send microdeposits.
000555555559pm_usBankAccount_dispute110000000The payment triggers a dispute.
000000000009pm_usBankAccount_processing110000000The payment stays in processing indefinitely. Useful for testing PaymentIntent cancellation.
000777777771pm_usBankAccount_weeklyLimitExceeded110000000The 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 values0.01 descriptor code valuesScenario
32 and 45SM11AASimulates verifying the account.
10 and 11SM33CCSimulates exceeding the number of allowed verification attempts.
40 and 41SM44DDSimulates 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.

OptionalGranular settlement speed
Server-side

OptionalInstant only verification
Server-side

OptionalMicrodeposit only verification
Server-side

OptionalPayment Reference

See also

  • Save ACH Direct Debit pre-authorized debit details for future payments
Was this page helpful?
YesNo
  • Need help? Contact Support.
  • Join our early access program.
  • Check out our changelog.
  • Questions? Contact Sales.
  • LLM? Read llms.txt.
  • Powered by Markdoc