# Accept a bank transfer Use the Payment Intents API to accept bank transfer payments. The first time you accept a bank transfer payment from a customer, Stripe generates a virtual bank account for them, 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 should include with each transfer to make it easier 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. You can find an overview of the common steps when accepting a bank transfer payment in the following sequence diagram: #### With Invoices Common steps when accepting a bank transfer payment (See full diagram at https://docs.stripe.com/payments/bank-transfers/accept-a-payment) #### Without Invoices ![](https://b.stripecdn.com/docs-statics-srv/assets/without-invoices-diagram-1.a17fe04695ef532427f25a9e1276ee2e.svg) ## 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). #### With Invoices When a customer underpays: A customer has sent a bank transfer for less than the expected amount (See full diagram at https://docs.stripe.com/payments/bank-transfers/accept-a-payment) When a customer overpays: A customer has sent a bank transfer for more than the expected amount (See full diagram at https://docs.stripe.com/payments/bank-transfers/accept-a-payment) #### Without Invoices ![](https://b.stripecdn.com/docs-statics-srv/assets/without-invoices-diagram-2.464fe916d5822422144c8aea0f31ed45.svg) ![](https://b.stripecdn.com/docs-statics-srv/assets/without-invoices-diagram-3.b1992851a5f3492d14eaa35e336b0e9f.svg) ## 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md) the bank transfer by using information like the transfer’s reference code or the amount transferred. You can disable automatic reconciliation and [manually reconcile](https://docs.stripe.com/payments/customer-balance/reconciliation.md#cash-manual-reconciliation) payments and invoices yourself. You can override the automatic reconciliation behavior on a per-customer basis by setting [reconciliation mode](https://docs.stripe.com/api/customers/create.md#create_customer-cash_balance-settings-reconciliation_mode) to manual. > If your integration uses invoices, see [Send an invoice with bank transfer instructions](https://docs.stripe.com/invoicing/bank-transfer.md). # Elements ## 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 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 [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. Create a customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) or [Customer](https://docs.stripe.com/api/customers/create.md) 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. #### Accounts v2 ```curl curl -X POST https://api.stripe.com/v2/core/accounts \ -H "Authorization: Bearer <>" \ -H "Stripe-Version: 2026-05-27.preview" \ --json '{ "contact_email": "jenny.rosen@example.com", "display_name": "Jenny Rosen", "configuration": { "customer": {} }, "include": [ "configuration.customer" ] }' ``` #### Customers v1 ```curl curl https://api.stripe.com/v1/customers \ -u "<>:" \ -d "name=Jenny Rosen" \ --data-urlencode "email=jenny.rosen@example.com" ``` ## Create a PaymentIntent [Server-side] A [PaymentIntent](https://docs.stripe.com/api/payment_intents/object.md) 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](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer) or [customer_account](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer_account) parameter of the `PaymentIntent` creation request. Bank transfers aren’t available on `PaymentIntents` that don’t have an associated customer. #### Manage payment methods from the Dashboard Before creating a Payment Intent, make sure to turn **Bank transfer** on in the [payment methods settings](https://dashboard.stripe.com/settings/payment_methods) page of your Dashboard. > With [Dynamic payment methods](https://docs.stripe.com/payments/payment-methods/dynamic-payment-methods.md), Stripe handles the return of eligible payment methods based on factors such as the transaction’s amount, currency, and payment flow. #### Accounts v2 #### US ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d currency=usd \ -d "automatic_payment_methods[enabled]=true" ``` #### UK ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d currency=gbp \ -d "automatic_payment_methods[enabled]=true" ``` #### EU ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d currency=eur \ -d "automatic_payment_methods[enabled]=true" ``` To localize the IBAN displayed to the customer, set the [`identity.individual.address`](https://docs.stripe.com/api/v2/core/accounts/object.md#v2_account_object-identity-individual-address) property or [edit the Account in the Dashboard](https://docs.stripe.com/invoicing/customer.md#edit-a-customer). We support IBAN localization for : - If you haven’t configured a country for your customer, or if their country doesn’t support IBAN localization, we fall back to the country of your Stripe account. - If IBAN localization isn’t supported for the country of your Stripe account, we fall back to `IE`. > Creating new localized virtual bank account numbers is currently unavailable for Spain (ES). Use any of the other EU VBAN countries instead. #### JP ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=19000 \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d currency=jpy \ -d "automatic_payment_methods[enabled]=true" \ --data-urlencode "return_url=https://example.com/return_url" ``` #### MX ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d currency=mxn \ -d "automatic_payment_methods[enabled]=true" \ --data-urlencode "return_url=https://example.com/return_url" ``` #### Customers v1 #### US ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d "customer={{CUSTOMER_ID}}" \ -d currency=usd \ -d "automatic_payment_methods[enabled]=true" ``` #### UK ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d "customer={{CUSTOMER_ID}}" \ -d currency=gbp \ -d "automatic_payment_methods[enabled]=true" ``` #### EU ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d "customer={{CUSTOMER_ID}}" \ -d currency=eur \ -d "automatic_payment_methods[enabled]=true" ``` To localize the IBAN displayed to the customer you can configure the country of the Customer by setting the [`address.country`](https://docs.stripe.com/api/customers/object.md#customer_object-address-country) property or by [editing the Customer in the Dashboard](https://docs.stripe.com/invoicing/customer.md#edit-a-customer). We support IBAN localization for : - If you haven’t configured a country for your Customer, or if their country doesn’t support IBAN localization, we fall back to the country of your Stripe account. - If IBAN localization isn’t supported for the country of your Stripe account, we fall back to `IE`. > Creating new localized virtual bank account numbers is currently unavailable for Spain (ES). Use any of the other EU VBAN countries instead. #### JP ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=19000 \ -d "customer={{CUSTOMER_ID}}" \ -d currency=jpy \ -d "automatic_payment_methods[enabled]=true" \ --data-urlencode "return_url=https://example.com/return_url" ``` #### MX ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d "customer={{CUSTOMER_ID}}" \ -d currency=mxn \ -d "automatic_payment_methods[enabled]=true" \ --data-urlencode "return_url=https://example.com/return_url" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). #### List payment methods manually To manually specify payment methods with the API, add `customer_balance` to the list of [payment method types](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_types) for your `PaymentIntent`. Specify the ID of the customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/object.md#v2_account_object-id) or [Customer](https://docs.stripe.com/api/customers/object.md#customer_object-id). #### Accounts v2 #### US ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=usd \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=us_bank_transfer" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `us_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. #### UK ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=gbp \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=gb_bank_transfer" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `gb_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. #### EU ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=eur \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=eu_bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][eu_bank_transfer][country]=FR" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `eu_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. - Set [payment_method_options[customer_balance][bank_transfer][eu_bank_transfer][country]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-eu_bank_account-country) to one of . The IBAN displayed to the customer is localized to the chosen country. > Creating new localized virtual bank account numbers is currently unavailable for Spain (ES). Use any of the other EU VBAN countries instead. #### JP ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=19000 \ -d currency=jpy \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=jp_bank_transfer" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `jp_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. #### MX ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=mxn \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=mx_bank_transfer" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `mx_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. #### Customers v1 #### US ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=usd \ -d "customer={{CUSTOMER_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=us_bank_transfer" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `us_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. #### UK ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=gbp \ -d "customer={{CUSTOMER_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=gb_bank_transfer" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `gb_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. #### EU ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=eur \ -d "customer={{CUSTOMER_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=eu_bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][eu_bank_transfer][country]=FR" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `eu_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. - Set [payment_method_options[customer_balance][bank_transfer][eu_bank_transfer][country]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-eu_bank_account-country) to one of . The IBAN displayed to the customer is localized to the chosen country. > Creating new localized virtual bank account numbers is currently unavailable for Spain (ES). Use any of the other EU VBAN countries instead. #### JP ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=19000 \ -d currency=jpy \ -d "customer={{CUSTOMER_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=jp_bank_transfer" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `jp_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. #### MX ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=mxn \ -d "customer={{CUSTOMER_ID}}" \ -d "payment_method_types[]=customer_balance" \ -d "payment_method_options[customer_balance][funding_type]=bank_transfer" \ -d "payment_method_options[customer_balance][bank_transfer][type]=mx_bank_transfer" ``` 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](https://docs.stripe.com/payments/customer-balance/reconciliation.md). - Use `bank_transfer` as the [funding_type](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-funding_type) to specify what to do when the customer doesn’t have enough balance to cover the payment amount. - Use `mx_bank_transfer` as the [bank_transfer[type]](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-customer_balance-bank_transfer-type) to specify the bank transfer types that can be used to fund the payment. > Always specify the `payment_method_types` when creating a payment. You can’t use `customer_balance` for [future payments](https://docs.stripe.com/payments/save-and-reuse.md). ## Collect payment details [Client-side] Collect payment details on the client with the [Payment Element](https://docs.stripe.com/payments/payment-element.md). 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](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allowpaymentrequest) 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](https://docs.stripe.com/security/guide.md#tls) when you’re ready to accept live payments. #### HTML + JS ### 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. ```html Checkout ``` Create an instance of Stripe with the following JavaScript on your checkout page: ```javascript // 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('<>'); ``` ### 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: ```html
``` When the previous form loads, create an instance of the Payment Element and mount it to the container DOM node. Pass the [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) from the previous step into `options` when you create the [Elements](https://docs.stripe.com/js/elements_object/create) 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. ```javascript 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 stepconst elements = stripe.elements(options); // Create and mount the Payment Element const paymentElementOptions = { layout: 'accordion'}; const paymentElement = elements.create('payment', paymentElementOptions); paymentElement.mount('#payment-element'); ``` #### React ### Set up Stripe.js Install [React Stripe.js](https://www.npmjs.com/package/@stripe/react-stripe-js) and the [Stripe.js loader](https://www.npmjs.com/package/@stripe/stripe-js) from the npm public registry: ```bash npm install --save @stripe/react-stripe-js @stripe/stripe-js ``` ### Add and configure the Elements provider to your payment page To use the Payment Element component, wrap your checkout page component in an [Elements provider](https://docs.stripe.com/sdks/stripejs-react.md#elements-provider). Call `loadStripe` with your publishable key, and pass the returned `Promise` to the `Elements` provider. Also pass the [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) from the previous step as `options` to the `Elements` provider. ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import {Elements} from '@stripe/react-stripe-js'; import {loadStripe} from '@stripe/stripe-js'; import CheckoutForm from './CheckoutForm'; // Make sure to call `loadStripe` outside of a component's render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe('<>'); function App() { const options = { // passing the client secret obtained in step 3 clientSecret: '{{CLIENT_SECRET}}', // Fully customizable with appearance API. appearance: {/*...*/}, }; return ( ); }; ReactDOM.render(, document.getElementById('root')); ``` ### Add the Payment Element component Use the `PaymentElement` component to build your form: ```jsx import React from 'react'; import {PaymentElement} from '@stripe/react-stripe-js'; const CheckoutForm = () => { return (
); }; export default CheckoutForm; ``` Stripe Elements is a collection of drop-in UI components. To further customize your form or collect different customer information, browse the [Elements docs](https://docs.stripe.com/payments/elements.md). 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](https://docs.stripe.com/js/elements_object/create#stripe_elements-options-appearance) 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](https://docs.stripe.com/api/tax/calculations/create.md) or entering shipping details, requires your customer’s full address. You can: - Use the [Address Element](https://docs.stripe.com/elements/address-element.md) 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](https://docs.stripe.com/js/payment_intents/confirm_payment) to complete the payment using details from the Payment Element. Provide a [return_url](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-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](https://docs.stripe.com/js/payment_intents/confirm_payment#confirm_payment_intent-options-redirect) to `if_required`. This only redirects customers that check out with redirect-based payment methods. #### HTML + JS ```javascript const form = document.getElementById('payment-form'); form.addEventListener('submit', async (event) => { event.preventDefault(); const {error} = await stripe.confirmPayment({ //`Elements` instance that was used to create the Payment Element elements, confirmParams: { return_url: 'https://example.com/order/123/complete', }, }); if (error) { // This point will only be reached if there is an immediate error when // confirming the payment. Show error to your customer (for example, payment // details incomplete) const messageContainer = document.querySelector('#error-message'); messageContainer.textContent = error.message; } else { // Your customer will be redirected to your `return_url`. For some payment // methods like iDEAL, your customer will be redirected to an intermediate // site first to authorize the payment, then redirected to the `return_url`. } }); ``` #### React To call [stripe.confirmPayment](https://docs.stripe.com/js/payment_intents/confirm_payment) from your payment form component, use the [useStripe](https://docs.stripe.com/sdks/stripejs-react.md#usestripe-hook) and [useElements](https://docs.stripe.com/sdks/stripejs-react.md#useelements-hook) hooks. If you prefer traditional class components over hooks, you can instead use an [ElementsConsumer](https://docs.stripe.com/sdks/stripejs-react.md#elements-consumer). ```jsx import React, {useState} from 'react'; import {useStripe, useElements, PaymentElement} from '@stripe/react-stripe-js'; const CheckoutForm = () => { const stripe = useStripe(); const elements = useElements(); const [errorMessage, setErrorMessage] = useState(null); const handleSubmit = async (event) => { // We don't want to let default form submission happen here, // which would refresh the page. event.preventDefault(); if (!stripe || !elements) { // Stripe.js hasn't yet loaded. // Make sure to disable form submission until Stripe.js has loaded. return; } const {error} = await stripe.confirmPayment({ //`Elements` instance that was used to create the Payment Element elements, confirmParams: { return_url: 'https://example.com/order/123/complete', }, }); if (error) { // This point will only be reached if there is an immediate error when // confirming the payment. Show error to your customer (for example, payment // details incomplete) setErrorMessage(error.message); } else { // Your customer will be redirected to your `return_url`. For some payment // methods like iDEAL, your customer will be redirected to an intermediate // site first to authorize the payment, then redirected to the `return_url`. } }; return (
{/* Show error message to your customers */} {errorMessage &&
{errorMessage}
} ); }; export default CheckoutForm; ``` 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: | Parameter | Description | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | | `payment_intent` | The unique identifier for the `PaymentIntent`. | | `payment_intent_client_secret` | The [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) of the `PaymentIntent` object. | > 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](https://docs.stripe.com/payments/paymentintents/lifecycle.md) 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. #### HTML + JS ```javascript // Initialize Stripe.js using your publishable key const stripe = Stripe('<>'); // Retrieve the "payment_intent_client_secret" query parameter appended to // your return_url by Stripe.js const clientSecret = new URLSearchParams(window.location.search).get( 'payment_intent_client_secret' ); // Retrieve the PaymentIntent stripe.retrievePaymentIntent(clientSecret).then(({paymentIntent}) => { const message = document.querySelector('#message') // Inspect the PaymentIntent `status` to indicate the status of the payment // to your customer. // // Some payment methods will [immediately succeed or fail][0] upon // confirmation, while others will first enter a `processing` state. // // [0]: https://stripe.com/docs/payments/payment-methods#payment-notification switch (paymentIntent.status) { case 'succeeded': message.innerText = 'Success! Payment received.'; break; case 'processing': message.innerText = "Payment processing. We'll update you when payment is received."; break; case 'requires_payment_method': message.innerText = 'Payment failed. Please try another payment method.'; // Redirect your user back to your payment page to attempt collecting // payment again break; default: message.innerText = 'Something went wrong.'; break; } }); ``` #### React ```jsx import React, {useState, useEffect} from 'react'; import {useStripe} from '@stripe/react-stripe-js'; const PaymentStatus = () => { const stripe = useStripe(); const [message, setMessage] = useState(null); useEffect(() => { if (!stripe) { return; } // Retrieve the "payment_intent_client_secret" query parameter appended to // your return_url by Stripe.js const clientSecret = new URLSearchParams(window.location.search).get( 'payment_intent_client_secret' ); // Retrieve the PaymentIntent stripe .retrievePaymentIntent(clientSecret) .then(({paymentIntent}) => { // Inspect the PaymentIntent `status` to indicate the status of the payment // to your customer. // // Some payment methods will [immediately succeed or fail][0] upon // confirmation, while others will first enter a `processing` state. // // [0]: https://stripe.com/docs/payments/payment-methods#payment-notification switch (paymentIntent.status) { case 'succeeded': setMessage('Success! Payment received.'); break; case 'processing': setMessage("Payment processing. We'll update you when payment is received."); break; case 'requires_payment_method': // Redirect your user back to your payment page to attempt collecting // payment again setMessage('Payment failed. Please try another payment method.'); break; default: setMessage('Something went wrong.'); break; } }); }, [stripe]); return message; }; export default PaymentStatus; ``` ## Optional: Send payment instruction emails You can enable Bank Transfer payment instruction emails from the [Dashboard](https://dashboard.stripe.com/settings/emails). After you enable payment instruction emails, Stripe sends your customer an email when: - A PaymentIntent is confirmed but the customer doesn’t have sufficient balance. - The customer sends a bank transfer but doesn’t have sufficient funds to complete the pending payments. A Bank Transfer payment instruction email contains the amount due, the bank information for transferring funds, and a link to the Stripe hosted instruction page. > In a sandbox, payment instruction emails are only sent to email addresses linked to the Stripe account. ## 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* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) 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](https://dashboard.stripe.com/webhooks/create). Alternatively, you can use the [Webhook Endpoints API](https://docs.stripe.com/api/webhook_endpoints.md) to start receiving the [payment_intent.partially_funded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.partially_funded) event. > The [Stripe CLI](https://docs.stripe.com/stripe-cli.md) 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. | Event | Description | Next steps | | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `payment_intent.requires_action` | Sent 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_funded` | The 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](https://docs.stripe.com/api/payment_intents/confirm.md) the PaymentIntent again. | | `payment_intent.succeeded` | The customer’s payment succeeded. | Fulfill the goods or services that the customer purchased. | > 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](https://docs.stripe.com/api/payment_intents/apply_customer_balance.md) again. We recommend [using webhooks](https://docs.stripe.com/payments/payment-intents/verifying-status.md#webhooks) to confirm the charge has succeeded and to notify the customer that the payment is complete. ### Sample code #### Ruby ```ruby 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) ) rescue JSON::ParserError => e # Invalid payload status 400 return end # Handle the event case event.type when 'payment_intent.requires_action' payment_intent = event.data.object # contains a Stripe::PaymentIntent # The payment intent was not fully funded due to insufficient funds # on the customer balance. Define and call a method to handle the payment intent. # handle_payment_intent_requires_action(payment_intent) when 'payment_intent.partially_funded' payment_intent = event.data.object # contains a Stripe::PaymentIntent # Then define and call a method to handle the payment intent being partially funded. # handle_payment_intent_partially_funded(payment_intent) when 'payment_intent.succeeded' payment_intent = event.data.object # contains a Stripe::PaymentIntent # Then define and call a method to handle the successful payment intent. # handle_payment_intent_succeeded(payment_intent) else puts "Unhandled event type: #{event.type}" end status 200 end ``` ### View pending payments in the Dashboard You can view all pending bank transfer PaymentIntents in the [Dashboard](https://dashboard.stripe.com/payments) 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. #### Dashboard To simulate a bank transfer using the Dashboard 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), go to the customer’s page in the Dashboard. Under **Payment methods**, click **Add** and select **Fund cash balance (test only)**. #### API To simulate a bank transfer using the API: #### US ```bash curl https://api.stripe.com/v1/test_helpers/customers/{{CUSTOMER_ID}}/fund_cash_balance \ -X POST \ -u <>: \ -d "reference"="REF-4242" \ -d "amount"="1000" \ -d "currency"="usd" ``` #### UK ```bash curl https://api.stripe.com/v1/test_helpers/customers/{{CUSTOMER_ID}}/fund_cash_balance \ -X POST \ -u <>: \ -d "reference"="REF-4242" \ -d "amount"="1000" \ -d "currency"="gbp" ``` #### EU ```bash curl https://api.stripe.com/v1/test_helpers/customers/{{CUSTOMER_ID}}/fund_cash_balance \ -X POST \ -u <>: \ -d "reference"="REF-4242" \ -d "amount"="1000" \ -d "currency"="eur" ``` #### JP ```bash curl https://api.stripe.com/v1/test_helpers/customers/{{CUSTOMER_ID}}/fund_cash_balance \ -X POST \ -u <>: \ -d "amount"="1000" \ -d "currency"="jpy" ``` #### MX ```bash curl https://api.stripe.com/v1/test_helpers/customers/{{CUSTOMER_ID}}/fund_cash_balance \ -X POST \ -u <>: \ -d "reference"="123456" \ -d "amount"="1000" \ -d "currency"="mxn" ``` #### Stripe CLI To simulate a bank transfer with the Stripe CLI: 1. [Install the Stripe CLI](https://docs.stripe.com/stripe-cli.md). 1. Log in to the CLI using the same account you logged in to the Stripe Dashboard with. ```bash stripe login ``` 1. Simulate an incoming bank transfer for an existing customer. #### US ```bash stripe test_helpers customers fund_cash_balance "{{CUSTOMER_ID}}" \ --amount=1099 \ --reference=DVGBG97TZ6ZV \ --currency=usd ``` The `reference` parameter is optional and simulates the value that the customer filled out for the bank transfer’s reference field. #### UK ```bash stripe test_helpers customers fund_cash_balance "{{CUSTOMER_ID}}" \ --amount=1099 \ --reference=DVGBG97TZ6ZV \ --currency=gbp ``` The `reference` parameter is optional and simulates the value that the customer filled out for the bank transfer’s reference field. #### EU ```bash stripe test_helpers customers fund_cash_balance "{{CUSTOMER_ID}}" \ --amount=1099 \ --reference=DVGBG97TZ6ZV \ --currency=eur ``` The `reference` parameter is optional and simulates the value that the customer filled out for the bank transfer’s reference field. #### JP ```bash stripe test_helpers customers fund_cash_balance "{{CUSTOMER_ID}}" \ --amount=1000 \ --currency=jpy ``` #### MX ```bash stripe test_helpers customers fund_cash_balance "{{CUSTOMER_ID}}" \ --amount=1099 \ --reference=123456 \ --currency=mxn ``` The `reference` parameter is optional and simulates the value that the customer filled out for the bank transfer’s reference field. ## Handling temporary availability issues The following error codes indicate temporary issues with the availability of the payment method: | Code | Description | Handling | | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `payment_method_rate_limit_exceeded` | Too many requests were made in quick succession for this payment method, which has stricter limits than the [API-wide rate limits](https://docs.stripe.com/rate-limits.md). | 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. | > If you anticipate heavy usage in general or because of an upcoming event, contact us as soon as you know about it.