Payments
Bank debits and transfers
SEPA Direct Debit
Accept a payment

Accept a SEPA Direct Debit payment

Learn to accept SEPA Direct Debit payments.

Accepting SEPA Direct Debit payments on your website consists of creating an object to track a payment, collecting payment method information and mandate acknowledgement, and submitting the payment to Stripe for processing. Stripe uses this payment object, the PaymentIntent, to track and handle all the states of the payment until the payment completes.

1 Determine compatibility

A Checkout Session must satisfy all of the following conditions to support SEPA Direct Debit payments:

  • You can only use one-time line items (recurring subscription plans are not supported).
  • Prices for all line items must be expressed in Euro (currency code eur).

2 Accept a payment

This guide walks you through enabling SEPA Direct Debit and shows the differences between accepting a card payment and using SEPA Direct Debit.

Enable SEPA Direct Debit as a payment method

When creating a new Checkout Session, you need to:

  1. Add sepa_debit to the list of payment_method_types.
  2. Make sure all your line_items use the eur currency.
Stripe::Checkout::Session.create({ mode: 'payment', payment_method_types: ['card'], payment_method_types: ['sepa_debit'], # or you can take multiple payment methods with # payment_method_types: ['card', 'sepa_debit', ...] line_items: [{ price_data: { currency: 'usd', # To accept `sepa_debit`, all line items must have currency: `eur` currency: 'eur', product_data: { name: 'T-shirt', }, unit_amount: 2000, }, quantity: 1, }], success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', })
stripe.checkout.Session.create( payment_method_types=['card'], payment_method_types=['sepa_debit'], # or you can take multiple payment methods with # payment_method_types=['card', 'sepa_debit', ...] line_items=[{ 'price_data': { currency: 'usd', # To accept `sepa_debit`, all line items must have currency: `eur` currency: 'eur', 'product_data': { 'name': 'T-shirt', }, 'unit_amount': 2000, }, 'quantity': 1, }], mode='payment', success_url='https://example.com/success', cancel_url='https://example.com/cancel', )
$session = \Stripe\Checkout\Session::create([ 'payment_method_types' => ['card'], 'payment_method_types' => ['sepa_debit'], // or you can take multiple payment methods with // 'payment_method_types' => ['card', 'sepa_debit', ...] 'line_items' => [[ 'price_data' => [ 'currency' => 'usd', # To accept `sepa_debit`, all line items must have currency: `eur` 'currency' => 'eur', 'product_data' => [ 'name' => 'T-shirt', ], 'unit_amount' => 2000, ], 'quantity' => 1, ]], 'mode' => 'payment', 'success_url' => 'https://example.com/success', 'cancel_url' => 'https://example.com/cancel', ]);
SessionCreateParams params = SessionCreateParams.builder() .addPaymentMethodType(SessionCreateParams.PaymentMethodType.CARD) .addPaymentMethodType(SessionCreateParams.PaymentMethodType.SEPA_DEBIT) .addLineItem( SessionCreateParams.LineItem.builder() .setPriceData( SessionCreateParams.LineItem.PriceData.builder() .setCurrency("usd") // To accept `sepa_debit`, all line items must have currency: `eur` .setCurrency("eur") .setUnitAmount(2000L) .setProductData( SessionCreateParams.LineItem.PriceData.ProductData.builder() .setName("T-shirt") .build()) .build()) .setQuantity(1L) .build()) .setMode(SessionCreateParams.Mode.PAYMENT) .setSuccessUrl("https://example.com/success") .setCancelUrl("https://example.com/cancel") .build(); Session session = Session.create(params);
const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], payment_method_types: ['sepa_debit'], // or you can take multiple payment methods with // payment_method_types: ['card', 'sepa_debit', ...] line_items: [{ price_data: { currency: 'usd', // To accept `sepa_debit`, all line items must have currency: `eur` currency: 'eur', product_data: { name: 'T-shirt', }, unit_amount: 2000, }, quantity: 1, }], mode: 'payment', success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', });
params := &stripe.CheckoutSessionParams{ PaymentMethodTypes: stripe.StringSlice([]string{ "card", "sepa_debit", }), LineItems: []*stripe.CheckoutSessionLineItemParams{ &stripe.CheckoutSessionLineItemParams{ PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{ Currency: stripe.String("usd"), // To accept `sepa_debit`, all line items must have currency: `eur` Currency: stripe.String("eur"), ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{ Name: stripe.String("T-shirt"), }, UnitAmount: stripe.Int64(2000), }, Quantity: stripe.Int64(1), }, }, Mode: stripe.String(string(stripe.CheckoutSessionModePayment)), SuccessURL: stripe.String("https://example.com/success"), CancelURL: stripe.String("https://example.com/cancel"), } s, _ := session.New(params)
var options = new SessionCreateOptions { PaymentMethodTypes = new List<string> { "card", "sepa_debit", }, LineItems = new List<SessionLineItemOptions> { new SessionLineItemOptions { PriceData = new SessionLineItemPriceDataOptions { UnitAmount = 2000, Currency = "usd", // To accept `sepa_debit`, all line items must have currency: `eur` Currency = "eur", ProductData = new SessionLineItemPriceDataProductDataOptions { Name = "T-shirt", }, }, Quantity = 1, }, }, Mode = "payment", SuccessUrl = "https://example.com/success", CancelUrl = "https://example.com/cancel", }; var service = new SessionService(); var session = service.Create(options);

Fulfill your orders

After accepting a payment, learn how to fulfill orders.

Because SEPA Direct Debit is a delayed notification payment method, you must also complete the handle delayed notification payment methods step of the guide.

3 Test your integration

Stripe provides several test numbers you can use to make sure your integration is ready for production.

Account Number Description
AT611904300234573201 Succeeds and instantly processes the payment.
AT321904300235473204 Succeeds and processes the payment after a 3-minute delay.
AT861904300235473202 Instantly fails the payment.
AT051904300235473205 Fails the payment after a 3-minute delay.
AT591904300235473203 The payment succeeded, but a dispute is immediately created.
Account Number Description
BE62510007547061 Succeeds and instantly processes the payment.
BE78510007547064 Succeeds and processes the payment after a 3-minute delay.
BE68539007547034 Instantly fails the payment.
BE51510007547065 Fails the payment after a 3-minute delay.
BE08510007547063 The payment succeeded, but a dispute is immediately created.
Account Number Description
EE382200221020145689 Succeeds and instantly processes the payment.
EE222200221020145682 Succeeds and processes the payment after a 3-minute delay.
EE762200221020145680 Instantly fails the payment.
EE922200221020145683 Fails the payment after a 3-minute delay.
EE492200221020145681 The payment succeeded, but a dispute is immediately created.
Account Number Description
FI2112345600000785 Succeeds and instantly processes the payment.
FI3712345600000788 Succeeds and processes the payment after a 3-minute delay.
FI9112345600000786 Instantly fails the payment.
FI1012345600000789 Fails the payment after a 3-minute delay.
FI6412345600000787 The payment succeeded, but a dispute is immediately created.
Account Number Description
FR1420041010050500013M02606 Succeeds and instantly processes the payment.
FR3020041010050500013M02609 Succeeds and processes the payment after a 3-minute delay.
FR8420041010050500013M02607 Instantly fails the payment.
FR7920041010050500013M02600 Fails the payment after a 3-minute delay.
FR5720041010050500013M02608 The payment succeeded, but a dispute is immediately created.
Account Number Description
DE89370400440532013000 Succeeds and instantly processes the payment.
DE08370400440532013003 Succeeds and processes the payment after a 3-minute delay.
DE62370400440532013001 Instantly fails the payment.
DE78370400440532013004 Fails the payment after a 3-minute delay.
DE35370400440532013002 The payment succeeded, but a dispute is immediately created.
Account Number Description
IE29AIBK93115212345678 Succeeds and instantly processes the payment.
IE24AIBK93115212345671 Succeeds and processes the payment after a 3-minute delay.
IE02AIBK93115212345679 Instantly fails the payment.
IE94AIBK93115212345672 Fails the payment after a 3-minute delay.
IE51AIBK93115212345670 The payment succeeded, but a dispute is immediately created.
Account Number Description
LT121000011101001000 Succeeds and instantly processes the payment.
LT281000011101001003 Succeeds and processes the payment after a 3-minute delay.
LT821000011101001001 Instantly fails the payment.
LT981000011101001004 Fails the payment after a 3-minute delay.
LT551000011101001002 The payment succeeded, but a dispute is immediately created.
Account Number Description
LU280019400644750000 Succeeds and instantly processes the payment.
LU440019400644750003 Succeeds and processes the payment after a 3-minute delay.
LU980019400644750001 Instantly fails the payment.
LU170019400644750004 Fails the payment after a 3-minute delay.
LU710019400644750002 The payment succeeded, but a dispute is immediately created.
Account Number Description
NL39RABO0300065264 Succeeds and instantly processes the payment.
NL55RABO0300065267 Succeeds and processes the payment after a 3-minute delay.
NL91ABNA0417164300 Instantly fails the payment.
NL28RABO0300065268 Fails the payment after a 3-minute delay.
NL82RABO0300065266 The payment succeeded, but a dispute is immediately created.
Account Number Description
NO9386011117947 Succeeds and instantly processes the payment.
NO8886011117940 Succeeds and processes the payment after a 3-minute delay.
NO6686011117948 Instantly fails the payment.
NO6186011117941 Fails the payment after a 3-minute delay.
NO3986011117949 The payment succeeded, but a dispute is immediately created.
Account Number Description
PT50000201231234567890154 Succeeds and instantly processes the payment.
PT66000201231234567890157 Succeeds and processes the payment after a 3-minute delay.
PT23000201231234567890155 Instantly fails the payment.
PT39000201231234567890158 Fails the payment after a 3-minute delay.
PT93000201231234567890156 The payment succeeded, but a dispute is immediately created.
Account Number Description
ES0700120345030000067890 Succeeds and instantly processes the payment.
ES2300120345030000067893 Succeeds and processes the payment after a 3-minute delay.
ES9121000418450200051332 Instantly fails the payment.
ES9300120345030000067894 Fails the payment after a 3-minute delay.
ES5000120345030000067892 The payment succeeded, but a dispute is immediately created.
Account Number Description
SE3550000000054910000003 Succeeds and instantly processes the payment.
SE5150000000054910000006 Succeeds and processes the payment after a 3-minute delay.
SE0850000000054910000004 Instantly fails the payment.
SE2450000000054910000007 Fails the payment after a 3-minute delay.
SE7850000000054910000005 The payment succeeded, but a dispute is immediately created.

4 Handle refunds and disputes

The refund period for SEPA Direct Debit is up to 180 days after the original payment.

Customers can dispute a payment through their bank up to 13 months after the original payment and there’s no appeal process.

Learn more about SEPA Direct Debit disputes.

Accepting SEPA Direct Debit payments on your website consists of creating an object to track a payment, collecting payment method information and mandate acknowledgement, and submitting the payment to Stripe for processing. Stripe uses this payment object, the PaymentIntent, to track and handle all the states of the payment until the payment completes.

1 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:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

2 Create or retrieve a Customer Server-side

To reuse a SEPA Direct Debit account for future payments, it must be attached to a Customer.

You should create a Customer object when your customer creates an account with your business. Associating the ID of the Customer object with your own internal representation of a customer will enable you to retrieve and use the stored payment method details later. If your customer is making a payment as a guest, you can still create a Customer object before payment and associate it with your internal representation of the customer’s account later.

Create a new Customer or retrieve an existing Customer to associate with this payment. Include the following code on your server to create a new Customer.

curl https://api.stripe.com/v1/customers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' customer = Stripe::Customer.create
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' customer = stripe.Customer.create()
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $customer = \Stripe\Customer::create();
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; CustomerCreateParams params = CustomerCreateParams.builder() .build(); Customer customer = Customer.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const customer = await stripe.customers.create();
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.CustomerParams{} c, _ := customer.New(params)
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new CustomerCreateOptions{}; var service = new CustomerService(); var customer = service.Create(options);

3 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. First, create a PaymentIntent on your server and specify the amount to collect and the eur currency (SEPA Direct Debit does not support other currencies). If you already have an integration using the Payment Intents API, add sepa_debit to the list of payment method types for your PaymentIntent. Specify the id of the Customer.

To save the SEPA Direct Debit account for reuse, set the setup_future_usage parameter to off_session. SEPA Direct Debit only accepts an off_session value for this parameter.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d setup_future_usage=off_session \ -d customer="{{CUSTOMER_ID}}" \ -d "payment_method_types[]"=sepa_debit \ -d "metadata[integration_checker]"=sepa_debit_accept_a_payment
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' Stripe::PaymentIntent.create({ amount: 1099, currency: 'eur', setup_future_usage: 'off_session', customer: customer['id'], payment_method_types: ['sepa_debit'], # Verify your integration in this guide by including this parameter metadata: {integration_check: 'sepa_debit_accept_a_payment'}, })
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' stripe.PaymentIntent.create( amount=1099, currency='eur', setup_future_usage='off_session', customer=customer['id'], payment_method_types=['sepa_debit'], # Verify your integration in this guide by including this parameter metadata={'integration_check': 'sepa_debit_accept_a_payment'}, )
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $intent = \Stripe\PaymentIntent::create([ 'amount' => 1099, 'currency' => 'eur', 'setup_future_usage' => 'off_session', 'customer' => $customer->id, 'payment_method_types' => ['sepa_debit'], 'metadata' => ['integration_check' => 'sepa_debit_accept_a_payment'], ]);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("eur") .setSetupFutureUsage(PaymentIntentCreateParams.SetupFutureUsage.OFF_SESSION) .setCustomer(customer.getId()) .addPaymentMethodType("sepa_debit") .putMetadata("integration_check", "sepa_debit_accept_a_payment") .build(); PaymentIntent intent = PaymentIntent.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const intent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', setup_future_usage: 'off_session', customer: customer.id, payment_method_types: ['sepa_debit'], // Verify your integration in this guide by including this parameter metadata: {integration_check: 'sepa_debit_accept_a_payment'}, });
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String((string(stripe.CurrencyEUR))), SetupFutureUsage: stripe.String(string(stripe.PaymentIntentSetupFutureUsageOffSession)), Customer: stripe.String(customer.ID), PaymentMethodTypes: stripe.StringSlice([]string{ "sepa_debit", }), } // Verify your integration in this guide by including this parameter params.AddMetadata("integration_check", "sepa_debit_accept_a_payment") pi, _ := paymentintent.New(params)
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "eur", SetupFutureUsage = "off_session", Customer = "{{CUSTOMER_ID}}", PaymentMethodTypes = new List<string> { "sepa_debit", }, // Verify your integration in this guide by including this parameter Metadata = new Dictionary<string, string> { { "integration_check", "sepa_debit_accept_a_payment" }, }, }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options);

4 Collect payment method details and mandate acknowledgment Client-side

You’re ready to collect payment information on the client with Stripe Elements. Elements is a set of prebuilt UI components for collecting payment details.

A Stripe Element contains an iframe that securely sends the payment information to Stripe over a HTTPS connection. The checkout page address must also start with https:// rather than http:// for your integration to work.

You can test your integration without using HTTPS. Enable it when you’re ready to accept live payments.

Set up Stripe Elements

Stripe Elements is automatically available as a feature of Stripe.js. Include the Stripe.js script on your payment page by adding it to the head of your HTML file. Always load Stripe.js directly from js.stripe.com to remain PCI compliant. Do not include the script in a bundle or host a copy of it yourself.

<head> <title>Submit Payment</title> <script src="https://js.stripe.com/v3/"></script> </head>

Create an instance of Elements with the following JavaScript on your payment page:

var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx'); var elements = stripe.elements();
const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx'); const elements = stripe.elements();

Add and configure an IBAN Element

Elements needs a place to live in your payment form. Create empty DOM nodes (container) with unique IDs in your payment form. Additionally, your customer must read and accept the SEPA Direct Debit mandate.

Display the following standard authorization text for your customer to implicitly sign this mandate. Replace Rocketship Inc with your company name.

The details of the accepted mandate are generated when setting up a PaymentMethod or confirming a PaymentIntent. This mandate must be communicated to your customer on the payment confirmation page or in an email.

<form action="/charge" method="post" id="payment-form"> <div class="form-row inline"> <div class="col"> <label for="accountholder-name"> Name </label> <input id="accountholder-name" name="accountholder-name" placeholder="Jenny Rosen" required /> </div> <div class="col"> <label for="email"> Email Address </label> <input id="email" name="email" type="email" placeholder="jenny.rosen@example.com" required /> </div> </div> <div class="form-row"> <!-- Using a label with a for attribute that matches the ID of the Element container enables the Element to automatically gain focus when the customer clicks on the label. --> <label for="iban-element"> IBAN </label> <div id="iban-element"> <!-- A Stripe Element will be inserted here. --> </div> </div> <!-- Add the client_secret from the PaymentIntent as a data attribute --> <button id="submit-button" data-secret="{CLIENT_SECRET}">Submit Payment</button> <!-- Display mandate acceptance text. --> <div id="mandate-acceptance"> By providing your payment information and confirming this payment, you authorise (A) Rocketship Inc and Stripe, our payment service provider, to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with those instructions. As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited. Your rights are explained in a statement that you can obtain from your bank. You agree to receive notifications for future debits up to 2 days before they occur. </div> <!-- Used to display form errors. --> <div id="error-message" role="alert"></div> </form>
/** * Shows how you can use CSS to style your Element's container. * These classes are added to your Stripe Element by default. * You can override these classNames by using the options passed * to the `iban` element. * https://stripe.com/docs/js/elements_object/create_element?type=iban#elements_create-options-classes */ input, .StripeElement { height: 40px; padding: 10px 12px; color: #32325d; background-color: white; border: 1px solid transparent; border-radius: 4px; box-shadow: 0 1px 3px 0 #e6ebf1; -webkit-transition: box-shadow 150ms ease; transition: box-shadow 150ms ease; } input:focus, .StripeElement--focus { box-shadow: 0 1px 3px 0 #cfd7df; } .StripeElement--invalid { border-color: #fa755a; } .StripeElement--webkit-autofill { background-color: #fefde5 !important; }

When the form loads, you can create an instance of the IBAN Element and mount it to the Element container:

// Custom styling can be passed to options when creating an Element. var style = { base: { color: '#32325d', fontSize: '16px', '::placeholder': { color: '#aab7c4' }, ':-webkit-autofill': { color: '#32325d', }, }, invalid: { color: '#fa755a', iconColor: '#fa755a', ':-webkit-autofill': { color: '#fa755a', }, }, }; var options = { style: style, supportedCountries: ['SEPA'], // Elements can use a placeholder as an example IBAN that reflects // the IBAN format of your customer's country. If you know your // customer's country, we recommend that you pass it to the Element as the // placeholderCountry. placeholderCountry: 'DE', }; // Create an instance of the IBAN Element var iban = elements.create('iban', options); // Add an instance of the IBAN Element into the `iban-element` <div> iban.mount('#iban-element');
// Custom styling can be passed to options when creating an Element. const style = { base: { color: '#32325d', fontSize: '16px', '::placeholder': { color: '#aab7c4' }, ':-webkit-autofill': { color: '#32325d', }, }, invalid: { color: '#fa755a', iconColor: '#fa755a', ':-webkit-autofill': { color: '#fa755a', }, }, }; const options = { style, supportedCountries: ['SEPA'], // Elements can use a placeholder as an example IBAN that reflects // the IBAN format of your customer's country. If you know your // customer's country, we recommend passing it to the Element as the // placeholderCountry. placeholderCountry: 'DE', }; // Create an instance of the IBAN Element const iban = elements.create('iban', options); // Add an instance of the IBAN Element into the `iban-element` <div> iban.mount('#iban-element');

Install React Stripe.js and the Stripe.js loader from the npm public registry.

npm install --save @stripe/react-stripe-js @stripe/stripe-js

We also provide a UMD build for sites that do not use npm or modules.

Include the Stripe.js script, which exports a global Stripe function, and the UMD build of React Stripe.js, which exports a global ReactStripe object. Always load the Stripe.js script directly from js.stripe.com to remain PCI compliant. Do not include the script in a bundle or host a copy of it yourself.

<!-- Stripe.js --> <script src="https://js.stripe.com/v3/"></script> <!-- React Stripe.js development build --> <script src="https://unpkg.com/@stripe/react-stripe-js@latest/dist/react-stripe.umd.js"></script> <!-- When you are ready to deploy your site to production, remove the above development script, and include the following production build. --> <script src="https://unpkg.com/@stripe/react-stripe-js@latest/dist/react-stripe.umd.min.js"></script>

Add Stripe.js and Elements to your page

To use Element components, wrap the root of your React app in an Elements provider. Call loadStripe with your publishable key and pass the returned Promise to the Elements provider.

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("pk_test_TYooMQauvdEDq54NiTphI7jx"); function App() { return ( <Elements stripe={stripePromise}> <CheckoutForm /> </Elements> ); }; ReactDOM.render(<App />, document.getElementById('root'));

Add and configure an IbanElement component

Use the IbanElement to collect your users IBAN. Additionally, your customer must read and accept the SEPA Direct Debit mandate.

Display the following standard authorization text for your customer to implicitly sign this mandate. Replace Rocketship Inc with your company name.

The details of the accepted mandate are generated when setting up a PaymentMethod or confirming a PaymentIntent. This mandate must be communicated to your customer on the payment confirmation page or in an email.

/** * Use the CSS tab above to style your Element's container. */ import React from 'react'; import {IbanElement} from '@stripe/react-stripe-js'; import './IbanFormStyles.css' // Custom styling can be passed as options when creating an Element. const IBAN_STYLE = { base: { color: '#32325d', fontSize: '16px', '::placeholder': { color: '#aab7c4' }, ':-webkit-autofill': { color: '#32325d', }, }, invalid: { color: '#fa755a', iconColor: '#fa755a', ':-webkit-autofill': { color: '#fa755a', }, } }; const IBAN_ELEMENT_OPTIONS = { supportedCountries: ['SEPA'], // Elements can use a placeholder as an example IBAN that reflects // the IBAN format of your customer's country. If you know your // customer's country, we recommend that you pass it to the Element as the // placeholderCountry. placeholderCountry: 'DE', style: IBAN_STYLE, }; export default function IbanForm({onSubmit, disabled}) { return ( <form onSubmit={onSubmit}> <div className="form-row inline"> <div className="col"> <label> Name <input name="accountholder-name" placeholder="Jenny Rosen" required /> </label> </div> <div className="col"> <label> Email Address <input name="email" type="email" placeholder="jenny.rosen@example.com" required /> </label> </div> </div> <div className="form-row"> <label> IBAN <IbanElement options={IBAN_ELEMENT_OPTIONS} /> </label> </div> <button type="submit" disabled={disabled}> Submit Payment </button> {/* Display mandate acceptance text. */} <div className="mandate-acceptance"> By providing your payment information and confirming this payment, you authorise (A) Rocketship Inc and Stripe, our payment service provider, to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with those instructions. As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited. Your rights are explained in a statement that you can obtain from your bank. You agree to receive notifications for future debits up to 2 days before they occur. </div> </form> ); };
/** * Shows how you can use CSS to style your Element's container. * These classes are added to your Stripe Element by default. * You can override these classNames by using the options passed * to the IbanElement component. * https://stripe.com/docs/js/elements_object/create_element?type=iban#elements_create-options-classes */ input, .StripeElement { height: 40px; padding: 10px 12px; color: #32325d; background-color: white; border: 1px solid transparent; border-radius: 4px; box-shadow: 0 1px 3px 0 #e6ebf1; -webkit-transition: box-shadow 150ms ease; transition: box-shadow 150ms ease; } input:focus, .StripeElement--focus { box-shadow: 0 1px 3px 0 #cfd7df; } .StripeElement--invalid { border-color: #fa755a; } .StripeElement--webkit-autofill { background-color: #fefde5 !important; }

Elements are completely customizable. You can style Elements to match the look and feel of your site, providing a seamless checkout experience for your customers. It’s also possible to style various input states, for example when the Element has focus.

5 Submit the payment to Stripe Client-side

Rather than sending the entire PaymentIntent object to the client, use its client secret from step 3. This is different from your API keys that authenticate Stripe API requests.

The client secret should still be handled carefully because it can complete the charge. Do not log it, embed it in URLs, or expose it to anyone but the customer.

Use stripe.confirmSepaDebitPayment to complete the payment when the user submits the form. Including the customer’s name and email address in the billing_details property of the payment_method parameter is required to create a SEPA Direct Debit PaymentMethod.

var form = document.getElementById('payment-form'); var accountholderName = document.getElementById('accountholder-name'); var email = document.getElementById('email'); var submitButton = document.getElementById('submit-button'); var clientSecret = submitButton.dataset.secret; form.addEventListener('submit', function(event) { event.preventDefault(); stripe.confirmSepaDebitPayment( clientSecret, { payment_method: { sepa_debit: iban, billing_details: { name: accountholderName.value, email: email.value, }, }, } ); });
const form = document.getElementById('payment-form'); const accountholderName = document.getElementById('accountholder-name'); const email = document.getElementById('email'); const submitButton = document.getElementById('submit-button'); const clientSecret = submitButton.dataset.secret; form.addEventListener('submit', (event) => { event.preventDefault(); stripe.confirmSepaDebitPayment( clientSecret, { payment_method: { sepa_debit: iban, billing_details: { name: accountholderName.value, email: email.value, }, }, } ); });

Use stripe.confirmSepaDebitPayment to complete the payment when the user submits the form. Including the customer’s name and email address in the billing_details property of the payment_method parameter is required to create a SEPA Direct Debit PaymentMethod.

To call stripe.confirmSepaDebitPayment from your payment form component, use the useStripe and useElements hooks.

If you prefer traditional class components over hooks, you can instead use an ElementsConsumer.

import React from 'react'; import {useStripe, useElements, IbanElement} from '@stripe/react-stripe-js'; import IbanForm from './IbanForm'; export default function CheckoutForm() { const stripe = useStripe(); const elements = useElements(); 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 has not yet loaded. // Make sure to disable form submission until Stripe.js has loaded. return; } const clientSecret = await getClientSecret(); const iban = elements.getElement(IbanElement); // For brevity, this example is using uncontrolled components for // the accountholder's name and email. In a real world app you will // probably want to use controlled components. // https://reactjs.org/docs/uncontrolled-components.html // https://reactjs.org/docs/forms.html#controlled-components const accountholderName = event.target['accountholder-name']; const email = event.target.email; const result = await stripe.confirmSepaDebitPayment('{CLIENT_SECRET}', { payment_method: { sepa_debit: iban, billing_details: { name: accountholderName.value, email: email.value, }, } }); if (result.error) { // Show error to your customer. console.log(result.error.message); } else { // Show a confirmation message to your customer. // The PaymentIntent is in the 'processing' state. // SEPA Direct Debit payments are asynchronous, // so funds are not immediately available. } }; return ( <IbanForm onSubmit={handleSubmit} disabled={!stripe} /> ); }
import React from 'react'; import {ElementsConsumer, IbanElement} from '@stripe/react-stripe-js'; import IbanForm from './IbanForm'; class CheckoutForm extends React.Component { handleSubmit = async (event) => { // We don't want to let default form submission happen here, // which would refresh the page. event.preventDefault(); const {stripe, elements} = this.props if (!stripe || !elements) { // Stripe.js has not yet loaded. // Make sure to disable form submission until Stripe.js has loaded. return; } const iban = elements.getElement(IbanElement); // For brevity, this example is using uncontrolled components for // the accountholder's name and email. In a real world app you will // probably want to use controlled components. // https://reactjs.org/docs/uncontrolled-components.html // https://reactjs.org/docs/forms.html#controlled-components const accountholderName = event.target['accountholder-name']; const email = event.target.email; const result = await stripe.confirmSepaDebitPayment('{CLIENT_SECRET}', { payment_method: { sepa_debit: iban, billing_details: { name: accountholderName.value, email: email.value, }, } }); if (result.error) { // Show error to your customer. console.log(result.error.message); } else { // Show a confirmation message to your customer. // The PaymentIntent is in the 'processing' state. // SEPA Direct Debit payments are asynchronous, // so funds are not immediately available. } }; render() { const {stripe} = this.props; return ( <IbanForm onSubmit={this.handleSubmit} disabled={!stripe} /> ); } } export default function InjectedCheckoutForm() { return ( <ElementsConsumer> {({stripe, elements}) => ( <CheckoutForm stripe={stripe} elements={elements} /> )} </ElementsConsumer> ); }

6 Confirm the PaymentIntent succeeded

SEPA Direct Debit is a delayed notification payment method, so funds are not immediately available. When the payment has been submitted successfully, the PaymentIntent status is updated from requires_confirmation to processing. After the payment has succeeded, the PaymentIntent status is updated from processing to succeeded.

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

Event Description Next steps
payment_intent.processing The customer’s payment was submitted to Stripe successfully. Wait for the initiated payment to succeed or fail.
payment_intent.succeeded The customer’s payment succeeded. Fulfill the goods or services that the customer purchased.
payment_intent.payment_failed The customer’s payment was declined. Contact the customer via email or push notification and request another payment method.

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

Note that because setup_future_usage and customer were set, the PaymentMethod will be attached to the Customer object once the payment enters the processing state. This attachment happens regardless of whether payment eventually succeeds or fails.

7 Test the integration

You can test your form using the test account numbers below with your confirmSepaDebitPayment request.

For the full list of test account numbers see our guide on testing.

Optional Validate the IBAN Element

The IBAN Element validates user input as it is typed. To help your customers catch mistakes, you should listen to change events on the IBAN Element and display any errors:

iban.on('change', function(event) { var displayError = document.getElementById('error-message'); if (event.error) { displayError.textContent = event.error.message; } else { displayError.textContent = ''; } });
iban.on('change', (event) => { const displayError = document.getElementById('error-message'); if (event.error) { displayError.textContent = event.error.message; } else { displayError.textContent = ''; } });
<IbanElement onChange={(event) => { if (event.error) { // Store event.error.message in state and display it. } else { // Remove existing error from state. } }}>

The change event contains other parameters that can help to build a richer user experience. Refer to the Stripe.js reference for more detail.

See also

Congrats, you are done with your integration! You can learn about saving bank account details without a payment and integrating with Connect.

Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.