# Set up a subscription with ACH Direct Debit Learn how to create and charge for a subscription with US bank account. > #### Use the Accounts v2 API to represent customers > > The Accounts v2 API is generally available for Connect users, and in public preview for other Stripe users. If you’re part of the Accounts v2 preview, you need to specify a [specify a preview version](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning) in your code. > > To request access to the Accounts v2 preview, > > For most use cases, we recommend [modeling your customers as customer-configured Account objects](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md) instead of using [Customer](https://docs.stripe.com/api/customers.md) objects. Use this guide to set up a *subscription* (A Subscription represents the product details associated with the plan that your customer subscribes to. Allows you to charge the customer on a recurring basis) using [ACH Direct Debit](https://docs.stripe.com/payments/ach-direct-debit.md). # Full hosted page If your Stripe Checkout integration allows customers to create subscriptions in a [Checkout Session](https://docs.stripe.com/api/checkout/sessions.md), you can add a US bank account as a payment method. A [Checkout Session](https://docs.stripe.com/api/checkout/sessions.md) represents the details of a customer’s intent to purchase. Create a Checkout Session when a customer wants to start a subscription. After redirecting a customer to a Checkout Session, Stripe presents a payment form where they can complete their purchase. After they complete a purchase, Stripe redirects them back to your site. After you complete the integration, you can extend it to: - [Add multiple prices](https://docs.stripe.com/products-prices/how-products-and-prices-work.md#multiple-prices) - [Create variable pricing](https://docs.stripe.com/products-prices/how-products-and-prices-work.md#variable-pricing) - [Handle existing customers](https://docs.stripe.com/payments/existing-customers.md?platform=web&ui=stripe-hosted) - [Handle trials](https://docs.stripe.com/billing/subscriptions/trials.md) - [Collect taxes](https://docs.stripe.com/billing/taxes/collect-taxes.md?tax-calculation=tax-rates) - [Create billing coupons](https://docs.stripe.com/billing/subscriptions/coupons.md) ## Set up Stripe [Server-side] First, you need a Stripe account. [Register now](https://dashboard.stripe.com/register). Use our official libraries for access to the Stripe API from your application: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ## Create the pricing model [Dashboard] [Stripe CLI] [Recurring pricing models](https://docs.stripe.com/products-prices/pricing-models.md) represent the products or services you sell, how much they cost, what currency you accept for payments, and the service period for subscriptions. To build the pricing model, create [products](https://docs.stripe.com/api/products.md) (what you sell) and [prices](https://docs.stripe.com/api/prices.md) (how much and how often to charge for your products). This example uses flat-rate pricing with two different service-level options: Basic and Premium. For each service-level option, you need to create a product and a recurring price. To add a one-time charge for something like a setup fee, create a third product with a one-time price. Each product bills at monthly intervals. The price for the Basic product is 5 USD. The price for the Premium product is 15 USD. See the [flat rate pricing](https://docs.stripe.com/subscriptions/pricing-models/flat-rate-pricing.md) guide for an example with three tiers. #### Dashboard Go to the [Add a product](https://dashboard.stripe.com/test/products/create) page and create two products. Add one price for each product, each with a monthly recurring billing period: - Premium product: Premium service with extra features - Price: Flat rate | 15 USD - Basic product: Basic service with minimum features - Price: Flat rate | 5 USD After you create the prices, record the price IDs so you can use them in other steps. Price IDs look like this: `price_G0FvDp6vZvdwRZ`. When you’re ready, use the **Copy to live mode** button at the top right of the page to clone your product from [a sandbox to live mode](https://docs.stripe.com/keys.md#test-live-modes). #### API You can use the API to create the [Products](https://docs.stripe.com/api/products.md) and [Prices](https://docs.stripe.com/api/prices.md). Create the Premium product: ```curl curl https://api.stripe.com/v1/products \ -u "<>:" \ --data-urlencode "name=Billing Guide: Premium Service" \ -d "description=Premium service with extra features" ``` Create the Basic product: ```curl curl https://api.stripe.com/v1/products \ -u "<>:" \ --data-urlencode "name=Billing Guide: Basic Service" \ -d "description=Basic service with minimum features" ``` Record the product ID for each product. They look like this: ```json { "id": "prod_H94k5odtwJXMtQ", "object": "product", "active": true, "attributes": [ ], "created": 1587577341, "description": "Premium service with extra features", "images": [ ], "livemode": false, "metadata": { }, "name": "Billing Guide: Premium Service", "statement_descriptor": null, "type": "service", "unit_label": null, "updated": 1587577341 } ``` Use the product IDs to create a price for each product. The [unit_amount](https://docs.stripe.com/api/prices/object.md#price_object-unit_amount) number is in cents, so `1500` = 15 USD, for example. Create the Premium price: ```curl curl https://api.stripe.com/v1/prices \ -u "<>:" \ -d product={{PREMIUM_PRODUCT_ID}} \ -d unit_amount=1500 \ -d currency=usd \ -d "recurring[interval]=month" ``` Create the Basic price: ```curl curl https://api.stripe.com/v1/prices \ -u "<>:" \ -d product={{BASIC_PRODUCT_ID}} \ -d unit_amount=500 \ -d currency=usd \ -d "recurring[interval]=month" ``` Record the price ID for each price so you can use them in subsequent steps. They look like this: ```json { "id": "price_HGd7M3DV3IMXkC", "object": "price", "product": "prod_HGd6W1VUqqXGvr", "type": "recurring", "currency": "usd", "recurring": { "interval": "month", "interval_count": 1, "trial_period_days": null, "usage_type": "licensed" }, "active": true, "billing_scheme": "per_unit", "created": 1589319695, "livemode": false, "lookup_key": null, "metadata": {}, "nickname": null, "unit_amount": 1500, "unit_amount_decimal": "1500", "tiers": null, "tiers_mode": null, "transform_quantity": null } ``` See examples of other [pricing models](https://docs.stripe.com/products-prices/pricing-models.md). ## Create a Checkout Session [Client-side] [Server-side] Add a checkout button to your website that calls a server-side endpoint to create a Checkout Session. ```html Checkout
``` ### Checkout Session parameters For a complete list of all the parameters you can use, see [Create a Session](https://docs.stripe.com/api/checkout/sessions/create.md). Create a Checkout Session with the ID of an existing [Price](https://docs.stripe.com/api/prices.md). Make sure that the mode is set to `subscription` and you pass at least one recurring price. You can add one-time prices in addition to recurring prices. After creating the Checkout Session, redirect your customer to the [URL](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-url) returned in the response. #### cURL ```bash curl https://api.stripe.com/v1/checkout/sessions \ -u <>: \ -d "payment_method_types[]"="us_bank_account" \ -d "line_items[][price]"="{{PRICE_ID}}" \ -d "line_items[][quantity]"=1 \ -d "mode"="subscription" \ -d "success_url"="https://example.com/success?session_id={CHECKOUT_SESSION_ID}" \ ``` When your customer successfully completes their payment, they’re redirected to the `success_url`, a page on your website that informs them that their payment was successful. Make the Session ID available on your success page by including the `{CHECKOUT_SESSION_ID}` template variable in the `success_url` as in the above example. Checkout Sessions expire 24 hours after creation by default. > Don’t rely on the redirect to the `success_url` alone for detecting payment initiation, because: > > - Malicious users could directly access the `success_url` without paying and gain access to your goods or services. - After a successful payment, customers might close their browser tab before they’re redirected to the `success_url`. ## Confirm the payment is successful When your customer completes a payment, Stripe redirects them to the URL that you specified in the `success_url` parameter. Typically, this is a page on your website that informs your customer that their payment was successful. However, US bank account debit is a delayed notification payment method, which means that funds aren’t immediately available. Because of this, delay order *fulfillment* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected) until the funds are available. After the payment succeeds, the underlying *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) status changes from `processing` to `succeeded`. A US bank account debit payment typically takes four days to make the funds available. You can confirm the payment is successful in several ways: #### Dashboard Successful payments display in the Dashboard’s [list of payments](https://dashboard.stripe.com/payments). When you click a payment, it takes you to the payment details page. The **Checkout summary** section contains billing information and the list of items purchased, which you can use to manually fulfill the order. ![](https://b.stripecdn.com/docs-statics-srv/assets/source.16d3029596357c80a8efdbbfe106108a.png) > Stripe can help you keep up with incoming payments by sending you email notifications whenever a customer successfully completes one. Use the Dashboard to [configure email notifications](https://dashboard.stripe.com/settings/user). #### Webhooks We send the following Checkout events when the payment status changes: | Event Name | Description | Next steps | | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | [checkout.session.completed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.completed) | The customer has successfully authorized the debit payment by submitting the Checkout form. | Wait for the payment to succeed or fail. | | [checkout.session.async_payment_succeeded](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.async_payment_succeeded) | The delayed payment method eventually succeeded. | Fulfill the goods or services that the customer purchased. | | [checkout.session.async_payment_failed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.async_payment_failed) | The delayed payment method eventually failed. | Email the customer and request that they attempt the payment again. | | [invoice.paid](https://docs.stripe.com/api/events/types.md#event_types-invoice.paid) | The customer’s payment succeeded. | Fulfill the goods or services that the customer purchased. | | [invoice.payment_failed](https://docs.stripe.com/api/events/types.md#event_types-invoice.payment_failed) | The customer’s payment was declined, or failed for some other reason. | Contact the customer through email and request that they attempt the payment again. | Your webhook code needs to handle all of these Checkout events. Each Checkout webhook payload includes the [Checkout Session object](https://docs.stripe.com/api/checkout/sessions.md) and invoice webhooks include the [Invoice](https://docs.stripe.com/api/invoices/object.md) object. Both contain information about the customer and *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods). Stripe sends the `checkout.session.completed` webhook to your server before redirecting your customer. Your webhook acknowledgement (any `2xx` status code) triggers the customer’s redirect to the `success_url`. If Stripe doesn’t receive successful acknowledgement within 10 seconds of a successful payment, your customer automatically redirects to the `success_url` page. On your `success_url` page, show a success message to your customer, and let them know that fulfillment of the order will take up to four days as the US bank account debit payment method isn’t instant. We recommend [using webhooks](https://docs.stripe.com/webhooks.md) to confirm the payment has succeeded and fulfill the goods or services the customer purchased. Below is an example webhook endpoint that handles the success or failure of a payment: #### Ruby ```ruby # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. # Find your keys at https://dashboard.stripe.com/apikeys. client = Stripe::StripeClient.new('<>') # You can find your endpoint's secret in your webhook settings endpoint_secret = 'whsec_...' # Using Sinatra post '/webhook' do payload = request.body.read event = nil # Verify webhook signature and extract the event # See https://stripe.com/docs/webhooks#verify-events for more information. sig_header = request.env['HTTP_STRIPE_SIGNATURE'] begin event = Stripe::Webhook.construct_event( payload, sig_header, endpoint_secret ) rescue JSON::ParserError => e # Invalid payload status 400 return rescue Stripe::SignatureVerificationError => e # Invalid signature status 400 return end case event['type'] when 'checkout.session.completed' session = event['data']['object'] subscription_id = session.subscription # Find the subscription or save it to your database. # invoice.paid may have fired before this so there # could already be a subscription. find_or_create_subscription(subscription_id) when 'invoice.paid' invoice = event['data']['object'] subscription_id = invoice.parent.subscription_details.subscription # Find the subscription or save it to your database. # checkout.session.completed may not have fired yet # so we may need to create the subscription. subscription = find_or_create_subscription(subscription_id) # Fulfill the purchase fulfill_order(invoice) # Record that the subscription has been paid for # this payment period. invoice.paid will fire every # time there is a payment made for this subscription. record_as_paid_for_this_period(subscription) when 'invoice.payment_failed' invoice = event['data']['object'] # Send an email to the customer asking them to retry their payment email_customer_about_failed_payment(invoice) end status 200 end ``` Get information about the customer, payment, or subscription by retrieving the objects referenced by the `customer` or `customer_account`, `payment_intent`, and `subscription` properties in the webhook payload. ### Retrieving line items from webhook By default, Checkout webhooks don’t return `line_items`. To retrieve the items created with the Checkout Session, make an additional request with the Checkout Session id: #### curl ```bash curl https://api.stripe.com/v1/checkout/sessions/{{CHECKOUT_SESSION_ID}}/line_items \ -u <>: ``` #### Stripe CLI ```bash stripe get /v1/checkout/sessions/{{CHECKOUT_SESSION_ID}}/line_items ``` ### Testing webhooks locally To test webhooks locally, you can use the [Stripe CLI](https://docs.stripe.com/stripe-cli.md). After you install it, you can forward events to your server: ```bash stripe listen --forward-to localhost:4242/webhook Ready! Your webhook signing secret is '{{WEBHOOK_SIGNING_SECRET}}' (^C to quit) ``` Learn more about [setting up webhooks](https://docs.stripe.com/webhooks.md). #### Third-party plugins You can use plugins such as [Zapier](https://stripe.com/works-with/zapier) to automate updating your purchase fulfillment systems with information from Stripe payments. Some examples of automation supported by plugins include: - Updating spreadsheets used for order tracking in response to successful payments - Updating inventory management systems in response to successful payments - Triggering notifications to internal customer service teams using email or chat applications ## Test your integration Learn how to test scenarios with instant verifications using [Financial Connections](https://docs.stripe.com/financial-connections/testing.md#web-how-to-use-test-accounts). ### 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* (A sandbox is an isolated test environment that allows you to test Stripe functionality in your account without affecting your live integration. Use sandboxes to safely experiment with new features and changes). 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 route correctly. If you don’t include the **+test\_email** suffix, we won’t send the email. > You must [set up your Stripe account](https://docs.stripe.com/get-started/account/set-up.md) 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_usBankAccount_success` | `110000000` | The payment succeeds. | | `000111111113` | `pm_usBankAccount_accountClosed` | `110000000` | The payment fails because the account is closed. | | `000000004954` | `pm_usBankAccount_riskLevelHighest` | `110000000` | The payment is blocked by Radar due to a [high risk of fraud](https://docs.stripe.com/radar/risk-evaluation.md#high-risk). | | `000111111116` | `pm_usBankAccount_noAccount` | `110000000` | The payment fails because no account is found. | | `000222222227` | `pm_usBankAccount_insufficientFunds` | `110000000` | The payment fails due to insufficient funds. | | `000333333335` | `pm_usBankAccount_debitNotAuthorized` | `110000000` | The payment fails because debits aren’t authorized. | | `000444444440` | `pm_usBankAccount_invalidCurrency` | `110000000` | The payment fails due to invalid currency. | | `000666666661` | `pm_usBankAccount_failMicrodeposits` | `110000000` | The payment fails to send microdeposits. | | `000555555559` | `pm_usBankAccount_dispute` | `110000000` | The payment triggers a dispute. | | `000000000009` | `pm_usBankAccount_processing` | `110000000` | The payment stays in processing indefinitely. Useful for testing [PaymentIntent cancellation](https://docs.stripe.com/api/payment_intents/cancel.md). | | `000777777771` | `pm_usBankAccount_weeklyLimitExceeded` | `110000000` | The payment fails due to payment amount causing the account to exceed its weekly payment volume limit. | | `000888888885` | | `110000000` | The payment fails because of a deactivated [tokenized account number](https://docs.stripe.com/financial-connections/tokenized-account-numbers.md). | 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](https://docs.stripe.com/billing/subscriptions/ach-debit.md#timing) to settle in your available balance. ## Additional considerations ### Microdeposit verification failure When a bank account is pending verification with microdeposits, the customer can fail to verify for three reasons: - The microdeposits failed to send to the customer’s bank account (this usually indicates a closed or unavailable bank account or incorrect bank account number). - The customer made 10 failed verification attempts for the account. Exceeding this limit means the bank account can no longer be verified or reused. - The customer failed to verify the bank account within 10 days. If the bank account fails verification for one of these reasons, you can [handle the `checkout.session.async_payment_failed` event](https://docs.stripe.com/api/events/types.md?event_types-invoice.payment_succeeded=#event_types-checkout.session.async_payment_failed) to contact the customer about placing a new order. ## Optional: Instant only verification By default, ACH Direct Debit payments allow your customers to use instant bank account verification or microdeposits. You can optionally require instant bank account verification only, using the `payment_method_options[us_bank_account][verification_method]` parameter when you create the Checkout session. #### Accounts v2 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d mode=subscription \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "payment_method_types[0]=card" \ -d "payment_method_types[1]=us_bank_account" \ -d "payment_method_options[us_bank_account][verification_method]=instant" \ -d "payment_method_options[us_bank_account][financial_connections][permissions][0]=payment_method" \ -d "line_items[0][price]={{PRICE_ID}}" \ -d "line_items[0][quantity]=1" \ --data-urlencode "success_url=https://example.com/success" ``` #### Customers v1 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d mode=subscription \ -d "customer={{CUSTOMER_ID}}" \ -d "payment_method_types[0]=card" \ -d "payment_method_types[1]=us_bank_account" \ -d "payment_method_options[us_bank_account][verification_method]=instant" \ -d "payment_method_options[us_bank_account][financial_connections][permissions][0]=payment_method" \ -d "line_items[0][price]={{PRICE_ID}}" \ -d "line_items[0][quantity]=1" \ --data-urlencode "success_url=https://example.com/success" ``` ## See also - [Customize your integration](https://docs.stripe.com/payments/checkout/customization.md) - [Manage subscriptions with the customer portal](https://docs.stripe.com/billing/subscriptions/build-subscriptions.md?payment-ui=checkout&ui=stripe-hosted)