ACH direct debit

ACH direct debit lets your United States end-users pay from a bank account instead of a card. It settles asynchronously, so a payment stays in progress for a few business days before it completes or fails. This guide covers bank verification, payment states, saved bank accounts, and off-session charges.

ACH builds on the same Payments API as cards: read Creating Payments first.

United States bank accounts, USD only

ACH direct debit works for United States bank accounts and only for payments in USD. A payment that allows ACH_DIRECT_DEBIT in any other currency is rejected with PAYMENT_ACH_REQUIRES_USD.

Accepting ACH on a payment

Pass ACH_DIRECT_DEBIT in supportedPaymentMethods when you create the payment. Include CREDIT_CARD too to offer both:

typescript
const result = await forte.projects.createPayment({
  projectId,
  userId,
  createPaymentRequest: {
    currency: "usd",
    lineItems: [{ description: "Annual plan", unitAmountCents: 120000, quantity: 1 }],
    supportedPaymentMethods: ["CREDIT_CARD", "ACH_DIRECT_DEBIT"],
  },
});

Confirm the returned stripeClientSecret with the Stripe Payment Element as you do for cards. See Confirming the payment. The Payment Element shows a "United States bank account" option next to the card form.

Bank verification

Before a bank account can be debited, the customer must verify that they own it. Forte automatically prefers the faster of two paths:

  • Instant verification. The customer links their bank by logging in through Stripe's hosted bank-link flow inside the Payment Element. Verification is immediate and the payment proceeds with no further action. This is the common path for major United States banks.
  • Verification with microdeposits (fallback). If instant verification isn't available: for example, the customer types their routing and account number manually: Stripe sends a small deposit to the account, which arrives in 1–2 business days. The customer then confirms that deposit to finish verifying.
Microdeposits complete later, on Stripe's hosted page

You don't build anything for the microdeposit step. Stripe emails the customer a link to its own hosted verification page where they enter the deposit amount; once they do, the payment continues on its own. You have nothing to handle in your checkout.

Payment lifecycle

An ACH payment moves through the same payment states as a card payment, but it spends real time in PROCESSING:

StateMeaning for ACH
DRAFTCreated, not yet confirmed by the customer. Expires after 24 hours if never confirmed.
PROCESSINGThe bank is being verified and/or the debit is settling. This can last a few business days. Forte does not cancel a payment while it's here: only unconfirmed DRAFT payments are swept.
COMPLETEDThe debit cleared. Fires the PAYMENT_COMPLETED trigger.
FAILEDVerification or the debit failed (for example, insufficient funds or account closed). Fires the PAYMENT_FAILED trigger.

Forte advances these states automatically as Stripe reports progress. Because the customer isn't watching when an ACH debit clears or bounces, react to the PAYMENT_COMPLETED / PAYMENT_FAILED triggers rather than the synchronous createPayment response. Don't grant access on PROCESSING: wait for COMPLETED.

Saving a bank account for reuse

Save a bank account to a user through the same setup flow as a card. You can then charge it later without collecting the details again. In the Forte console, open the user and select Add payment method. The Payment Element offers United States bank account and saves it after verification. In code, start the setup and confirm it with Stripe Elements. Forte then attaches the verified account to the user.

Saved bank accounts appear in listPaymentMethods with type: "us_bank_account", the bankName, the account last4, and accountType (checking/savings):

json
{
  "id": "pm_...",
  "type": "us_bank_account",
  "bankName": "STRIPE TEST BANK",
  "last4": "6789",
  "accountType": "checking",
  "isDefault": false
}
A saved bank account may still be verifying

If the account needed microdeposits, Forte saves it but can't charge it until the customer finishes verifying. The Forte console shows a "verification pending" state when this happens.

Charging a saved method off-session (server-initiated)

Once a bank account is saved and verified, charge it without the customer present by passing offSession: true and its paymentMethodId: the same off-session flow as a saved card:

typescript
const result = await forte.projects.createPayment({
  projectId,
  userId,
  createPaymentRequest: {
    currency: "usd",
    lineItems: [{ description: "Usage: May", unitAmountCents: 4200, quantity: 1 }],
    paymentMethodId: "pm_...",
    offSession: true,
  },
});
 
// result.payment.state === "PROCESSING": wait for the PAYMENT_COMPLETED / PAYMENT_FAILED trigger.

Unlike a card, an off-session ACH charge can't decline immediately: it returns PROCESSING and can still fail days later, which is why the PAYMENT_FAILED trigger matters. For recurring billing, use Subscriptions (note: subscriptions are card-only today).

Testing ACH in sandbox

In a sandbox project (Stripe Test mode), use Stripe's test bank account: routing number 110000000, account number 000123456789: in the Payment Element to simulate a successful ACH payment. Stripe provides more test account numbers that simulate failures and the microdeposit flow.

Search

Search your resources, console pages, and documentation