Stripe | Financial Infrastructure to Grow Your Revenue

Stripe | Financial Infrastructure to Grow Your Revenue

4466 articles

Accept a bank transfer


Accept a bank transfer

Use the Checkout Sessions or Payment Intents API to accept bank transfer payments.

The first time you accept a bank transfer payment, Stripe generates a virtual bank account for the customer, which you can then share with them directly. All future bank transfer payments from this customer get sent to this bank account. In some countries, Stripe also provides you with a unique transfer reference number that your customer can include with each transfer to match the transfer against outstanding payments. Some countries have limits on the number of virtual bank account numbers that you can create for free.

Bank transfer payments overview

Handling underpayments and overpayments

With bank transfer payments, it’s possible that the customer sends you more or less than the expected payment amount. If the customer sends too little, Stripe partially funds an open Payment Intent. Invoices won’t be partially funded and remain open until incoming funds cover the full invoice amount.

If the customer sends more than the expected amount, Stripe attempts to reconcile the incoming funds against an open payment and keep the remaining excess amount in the customer balance. Learn more about how Stripe handles reconciliation.

Handling multiple open payments or invoices

You might have multiple open payments or invoices which can be paid with a bank transfer. In the default setup, Stripe attempts to automatically reconcile the bank transfer by using information like the transfer’s reference code or the amount transferred.

You can disable automatic reconciliation and manually reconcile payments and invoices yourself. You can override the automatic reconciliation behavior on a per-customer basis by setting reconciliation mode to manual.

Elements

Checkout

Direct API

Set up Stripe Server-side

First, you need a Stripe account. Register now.

Use our official libraries for access to the Stripe API from your application:

Command Line

Select a language

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

# Available as a gem
sudo gem install stripe

Gemfile

Select a language

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

# If you use bundler, you can add this line to your Gemfile
gem 'stripe'

Create or retrieve a customer Server-side

You must associate an object that represents your customer to reconcile each bank transfer payment. If you have an existing customer, you can skip this step. Otherwise, create a new one.

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 preview version in your code.

To join the Accounts v2 preview, go to Account previews and features in your Dashboard and enable Reusable payment methods for Global Payouts.

For most use cases, we recommend modeling your customers as customer-configured Account objects instead of using Customer objects.

Create a customer-configured Account or Customer when your customer creates an account with your business, or when saving a payment method. Associate the object’s ID with your own internal representation of a customer.

Create a new customer or retrieve an existing one to associate with this payment.

Command Line

Select a language

cURL

Stripe CLI

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

Create a PaymentIntent Server-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. Create a PaymentIntent on the server, specifying the amount and currency you want to collect. You must also populate the customer or customer_account parameter of the PaymentIntent creation request. Bank transfers aren’t available on PaymentIntents that don’t have an associated customer.

Before creating a Payment Intent, make sure to turn Bank transfer on in the payment methods settings page of your Dashboard.

Note

With Dynamic payment methods, Stripe handles the return of eligible payment methods based on factors such as the transaction’s amount, currency, and payment flow.

Command Line

Select a language

cURL

Stripe CLI

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

In the latest version of the API, specifying the automatic_payment_methods parameter is optional because Stripe enables its functionality by default.

If the customer already has a balance high enough to cover the payment amount, the PaymentIntent immediately succeeds with a succeeded status. Customers can accrue a balance when they accidentally overpay for a transaction—a common occurrence with bank transfers. You must reconcile customer balances within a certain period based on your location.

Collect payment details Client-side

Collect payment details on the client with the Payment Element. The Payment Element is a prebuilt UI component that simplifies collecting payment details for a variety of payment methods.

The Payment Element contains an iframe that securely sends payment information to Stripe over an HTTPS connection. Avoid placing the Payment Element within another iframe because some payment methods require redirecting to another page for payment confirmation.

If you choose to use an iframe and want to accept Apple Pay or Google Pay, the iframe must have the allow attribute set to equal "payment *".

The checkout page address must start with https:// rather than http:// for your integration to work. You can test your integration without using HTTPS, but remember to enable it when you’re ready to accept live payments.

Set up Stripe.js

The Payment Element is automatically available as a feature of Stripe.js. Include the Stripe.js script on your checkout page by adding it to the head of your HTML file. Always load Stripe.js directly from js.stripe.com to remain PCI compliant. Don’t include the script in a bundle or host a copy of it yourself.

checkout.html

Create an instance of Stripe with the following JavaScript on your checkout page:

checkout.js

// Set your publishable key: remember to change this to your live publishable key in production
// See your keys here: https://dashboard.stripe.com/apikeys
const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');

Add the Payment Element to your payment page

The Payment Element needs a place to live on your payment page. Create an empty DOM node (container) with a unique ID in your payment form:

checkout.html

<form id="payment-form">
 <div id="payment-element">
 <!-- Elements will create form elements here -->
 </div>
 <button id="submit">Submit</button>
 <div id="error-message">
 <!-- Display error message to your customers here -->
 </div>
</form>

When the previous form loads, create an instance of the Payment Element and mount it to the container DOM node. Pass the client secret from the previous step into options when you create the Elements instance:

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.

checkout.js

const options = {
 clientSecret: '{{CLIENT_SECRET}}',
 // Fully customizable with appearance API.
 appearance: {/*...*/},
};

// Set up Stripe.js and Elements to use in checkout form, passing the client secret obtained in a previous step
const elements = stripe.elements(options);

// Create and mount the Payment Element
const paymentElementOptions = { layout: 'accordion'};
const paymentElement = elements.create('payment', paymentElementOptions);
paymentElement.mount('#payment-element');

The Payment Element renders a dynamic form that allows your customer to pick a payment method. For each payment method, the form automatically asks the customer to fill in all necessary payment details.

Customize appearance

Customize the Payment Element to match the design of your site by passing the appearance object into options when creating the Elements provider.

Collect addresses

By default, the Payment Element only collects the necessary billing address details. Some behavior, such as calculating tax or entering shipping details, requires your customer’s full address. You can:

  • Use the Address Element to take advantage of autocomplete and localization features to collect your customer’s full address. This helps ensure the most accurate tax calculation.
  • Collect address details using your own custom form.

Upon confirmation, Stripe automatically opens a modal to display the bank transfer details to your customer.

Submit payment to Stripe Client-side

Use stripe.confirmPayment to complete the payment using details from the Payment Element. Provide a return_url to this function to indicate where Stripe should redirect the user after they complete the payment. Your user may be first redirected to an intermediate site, like a bank authorization page, before being redirected to the return_url. Card payments immediately redirect to the return_url when a payment is successful.

If you don’t want to redirect for card payments after payment completion, you can set redirect to if_required. This only redirects customers that check out with redirect-based payment methods.

checkout.js

Make sure the return_url corresponds to a page on your website that provides the status of the payment. When Stripe redirects the customer to the return_url, we provide the following URL query parameters:

ParameterDescription
payment_intentThe unique identifier for the PaymentIntent.
payment_intent_client_secretThe client secret of the PaymentIntent object.

Caution

If you have tooling that tracks the customer’s browser session, you might need to add the stripe.com domain to the referrer exclude list. Redirects cause some tools to create new sessions, which prevents you from tracking the complete session.

Use one of the query parameters to retrieve the PaymentIntent. Inspect the status of the PaymentIntent to decide what to show your customers. You can also append your own query parameters when providing the return_url, which persist through the redirect process.

status.js

Collect wallet information

If you accept Apple Pay or Google Pay, call elements.submit() at the beginning of your submission handler. This triggers form validation and shows the native payment sheet so your customer can authorize the payment.

checkout.js

form.addEventListener('submit', async (event) => {
 event.preventDefault();

 // Trigger form validation and wallet collection
 const {error: submitError} = await elements.submit();
 if (submitError) {
 handleError(submitError);
 return;
 }

 // Confirm the payment
});

Optional Send payment instruction emails

Confirm the PaymentIntent succeeded

The PaymentIntent stays in a requires_action status until funds arrive in the bank account. When funds are ready, the PaymentIntent status updates from requires_action to succeeded.

Your webhook endpoint needs to be set up to receive the payment_intent.partially_funded event. Stripe sends this event if funds are applied to a PaymentIntent but the payment remains incomplete. Note that when you first confirm a PaymentIntent and it transitions to requires_action, Stripe sends payment_intent.requires_action instead, even if existing customer balance funds were applied at the same time.

You can add a webhook from the Dashboard.

Alternatively, you can use the Webhook Endpoints API to start receiving the payment_intent.partially_funded event.

Caution

The Stripe CLI doesn’t support triggering beta API version events, such as payment_intent.partially_funded.

The following events are sent during the payment funding flow when the PaymentIntent is updated.

EventDescriptionNext steps
payment_intent.requires_actionSent during confirmation when the customer balance doesn’t have sufficient funds to reconcile the PaymentIntent, the PaymentIntent transitions to requires_action.Instruct your customer to send a bank transfer with the amount_remaining.
payment_intent.partially_fundedThe customer sent a bank transfer that was applied to the PaymentIntent, but it wasn’t enough to complete the payment. This might happen because the customer underpaid, bank fees reduced the amount received, or a remaining customer balance was applied to the PaymentIntent. PaymentIntents that are partially funded aren’t reflected in your account balance until the payment is complete.Instruct your customer to send another bank transfer with the new amount_remaining to complete the payment. If you want to complete the payment with the partially applied funds, you can update the amount and confirm the PaymentIntent again.
payment_intent.succeededThe customer’s payment succeeded.Fulfill the goods or services that the customer purchased.

Caution

When you change the amount of a partially funded PaymentIntent, the funds are returned to the customer balance. If other PaymentIntents are open, Stripe funds those automatically. If the customer is configured for manual reconciliation, you need to apply the funds again.

We recommend using webhooks to confirm the charge has succeeded and to notify the customer that the payment is complete.

Sample code

Select a language

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

require 'json'

# Using Sinatra
post '/webhook' do
 payload = request.body.read
 event = nil

 begin
 event = Stripe::Event.construct_from(
 JSON.parse(payload, symbolize_names: true)

View pending payments in the Dashboard

You can view all pending bank transfer PaymentIntents in the Dashboard by applying the Waiting on funding filter to Status.

Test your integration

You can test your integration by simulating an incoming bank transfer using the API, Dashboard, or a beta version of the Stripe CLI.

Accounts v2 considerations

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 preview version in your code.

To simulate an incoming bank transfer for a customer-configured Account, use the Test Helpers endpoint with the Account ID as the path parameter instead of the Customer ID, for example the relevant part of the product.

To simulate a bank transfer using the Dashboard in a sandbox, go to the customer’s page in the Dashboard. Under Payment methods, click Add and select Fund cash balance (test only).

Handling temporary availability issues

The following error codes indicate temporary issues with the availability of the payment method:

CodeDescriptionHandling
payment_method_rate_limit_exceededToo many requests were made in quick succession for this payment method, which has stricter limits than the API-wide rate limits.These errors can persist for several API requests when many of your customers try to use the same payment method, such as during an ongoing sale on your website. In this case, ask your customers to choose a different payment method.

Caution

If you anticipate heavy usage in general or because of an upcoming event, contact us as soon as you know about it.

Last verified 2026-09-24

Is this helpful?