Accept a SEPA Direct Debit payment

    Use the Payment Intents and Payment Methods APIs to accept SEPA Direct Debit payments.

    Overview

    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 sudo 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/
    # 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
    # Install via go go get github.com/stripe/stripe-go
    // Then import the package import ( "github.com/stripe/stripe-go" )
    # Install via dotnet dotnet add package Stripe.net dotnet restore
    # Or install via NuGet PM> Install-Package Stripe.net

    2 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.

    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 setup_future_usage=off_session \ -d currency=eur \ -d "payment_method_types[]"=sepa_debit
    # 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' intent = Stripe::PaymentIntent.create({ amount: 1099, currency: 'eur', setup_future_usage: 'off_session', payment_method_types: ['sepa_debit'], })
    # 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', payment_method_types=['sepa_debit'], )
    // 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'); \Stripe\PaymentIntent::create([ 'amount' => 1099, 'currency' => 'eur', 'setup_future_usage' => 'off_session', 'payment_method_types' => ['sepa_debit'], ]);
    // 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"; Map<String, Object> params = new HashMap<String, Object>(); params.put("amount", 1099); params.put("currency", "eur"); params.put("setup_future_usage", "off_session"); params.put("payment_method_types", Arrays.asList("sepa_debit")); 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 paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', setup_future_usage: 'off_session', payment_method_types: ['sepa_debit'], });
    // 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("off_session"), PaymentMethodTypes: stripe.StringSlice([]string{ "sepa_debit", }), } 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 service = new PaymentIntentService(); var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "eur", SetupFutureUsage = "off_session", PaymentMethodTypes = new List<string> { "sepa_debit", }, }; var intent = service.Create(options);

    3 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> <!-- Used to display form errors. --> <div id="error-message" role="alert"></div> <!-- Display mandate acceptance text. --> <div id="mandate-acceptance"> By providing your IBAN and confirming this payment, you are authorizing Rocketship Inc. and Stripe, our payment service provider, to send instructions to your bank to debit your account in accordance with those instructions. 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 eight weeks starting from the date on which your account was debited. </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 @stripe/react-stripe-js and @stripe/stripe-js:

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

    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 IBAN and confirming this payment, you are authorizing Rocketship Inc. and Stripe, our payment service provider, to send instructions to your bank to debit your account in accordance with those instructions. 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 eight weeks starting from the date on which your account was debited. </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.

    4 Submit the payment to Stripe Client-side

    Rather than sending the entire PaymentIntent object to the client, use its client secret from step 1. 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> ); }

    5 Save the payment method details Server-side

    To save the payment method details, create a new Customer object and pass the PaymentMethod ID.

    curl https://api.stripe.com/v1/customers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d payment_method="{{PAYMENT_METHOD_ID}}"
    # 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::Customer.create({ payment_method: '{{PAYMENT_METHOD_ID}}', })
    # 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.Customer.create( payment_method='{{PAYMENT_METHOD_ID}}', )
    // 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'); \Stripe\Customer::create([ 'payment_method' => '{{PAYMENT_METHOD_ID}}', ]);
    // 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"; Map<String, Object> params = new HashMap<String, Object>(); params.put("payment_method", "{{PAYMENT_METHOD_ID}}"); 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({ paymentMethod: '{{PAYMENT_METHOD_ID}}', });
    // 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{ PaymentMethod: stripe.String("{{PAYMENT_METHOD_ID}}"), } 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 { PaymentMethod = "{{PAYMENT_METHOD_ID}}", }; var service = new CustomerService(); var customer = service.Create(options);

    If you have an existing Customer object, you can do either of the following:

    • Attach the PaymentMethod to the existing Customer object as a separate request
    curl https://api.stripe.com/v1/payment_methods/{{PAYMENT_METHOD_ID}}/attach \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d customer="{{CUSTOMER_ID}}"
    # 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' intent = Stripe::PaymentMethod.attach( '{{PAYMENT_METHOD_ID}}', { customer: '{{CUSTOMER_ID}}', } )
    # 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.PaymentMethod.attach( '{{PAYMENT_METHOD_ID}}', customer='{{CUSTOMER_ID}}', )
    // 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'); \Stripe\PaymentMethod::retrieve('{{PAYMENT_METHOD_ID}}'); $payment_method->attach([ 'customer' => '{{CUSTOMER_ID}}', ]);
    // 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"; PaymentMethod paymentMethod = PaymentMethod.retrieve("{{PAYMENT_METHOD_ID}}"); Map<String, Object> params = new HashMap<String, Object>(); params.put("customer", "{{CUSTOMER_ID}}"); paymentMethod = paymentMethod.attach(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 paymentMethod = await stripe.paymentMethods.attach( '{{PAYMENT_METHOD_ID}}', { customer: '{{CUSTOMER_ID}}', } );
    // 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.PaymentMethodAttachParams{ Customer: stripe.String("{{CUSTOMER_ID}}"), } paymentMethod, _ := paymentmethod.Attach( "{{PAYMENT_METHOD_ID}}", 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 PaymentMethodAttachOptions { Customer = "{{CUSTOMER_ID}}", }; var service = new PaymentMethodService(); var paymentMethod = service.Attach("{{PAYMENT_METHOD_ID}}", options);
    • Attach the PaymentIntent to the existing Customer object while making the payment
    stripe.confirmSepaDebitPayment( clientSecret, { payment_method: { sepa_debit: iban, billing_details: { name: accountholderName.value, email: email.value, }, }, customer: '{CUSTOMER_ID}', // Specify `save_payment_method` to save the PaymentMethod // as part of making a payment save_payment_method: true, } );

    Note that the save_payment_method parameter attaches the PaymentMethod to the Customer object regardless of whether the payment succeeds or fails.

    After you attach the PaymentMethod or PaymentIntent to the existing Customer object, you can associate the ID of the Customer object with your internal representation of a customer. This allows you to use the stored PaymentMethod to collect future payments without prompting for your customer’s bank account details.

    6 Confirm the PaymentIntent succeeded

    SEPA Direct Debit payments are asynchronous, so funds are not immediately available. 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 Expected Integration
    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.

    7 Test the integration

    You can create a test PaymentIntent that either succeeds or fails by doing the following:

    1. Create a test PaymentMethod with a test account number.
    2. Use the resulting PaymentMethod in a confirmSepaDebitPayment request to create the test charge.
    Test account numbers
    Account Number Description
    AT611904300234573201 The PaymentIntent status transitions from processing to succeeded.
    AT861904300235473202 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    AT591904300235473203 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    BE62510007547061 The PaymentIntent status transitions from processing to succeeded.
    BE68539007547034 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    BE08510007547063 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    DK5000400440116243 The PaymentIntent status transitions from processing to succeeded.
    DK8003450003179681 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    DK9300400440116245 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    EE382200221020145689 The PaymentIntent status transitions from processing to succeeded.
    EE762200221020145680 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    EE492200221020145681 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    FI2112345600000785 The PaymentIntent status transitions from processing to succeeded.
    FI9112345600000786 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    FI6412345600000787 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    FR1420041010050500013M02606 The PaymentIntent status transitions from processing to succeeded.
    FR8420041010050500013M02607 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    FR5720041010050500013M02608 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    DE89370400440532013000 The PaymentIntent status transitions from processing to succeeded.
    DE62370400440532013001 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    DE35370400440532013002 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    IE29AIBK93115212345678 The PaymentIntent status transitions from processing to succeeded.
    IE02AIBK93115212345679 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    IE51AIBK93115212345670 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    IT40S0542811101000000123456 The PaymentIntent status transitions from processing to succeeded.
    IT60X0542811101000000123456 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    IT83S0542811101000000123458 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    LT121000011101001000 The PaymentIntent status transitions from processing to succeeded.
    LT821000011101001001 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    LT551000011101001002 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    LU280019400644750000 The PaymentIntent status transitions from processing to succeeded.
    LU980019400644750001 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    LU710019400644750002 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    NL39RABO0300065264 The PaymentIntent status transitions from processing to succeeded.
    NL91ABNA0417164300 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    NL82RABO0300065266 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    NO9386011117947 The PaymentIntent status transitions from processing to succeeded.
    NO6686011117948 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    NO3986011117949 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    PT50000201231234567890154 The PaymentIntent status transitions from processing to succeeded.
    PT23000201231234567890155 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    PT93000201231234567890156 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    ES0700120345030000067890 The PaymentIntent status transitions from processing to succeeded.
    ES9121000418450200051332 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    ES5000120345030000067892 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    SE3550000000054910000003 The PaymentIntent status transitions from processing to succeeded.
    SE0850000000054910000004 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    SE7850000000054910000005 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
    Account Number Description
    GB82WEST12345698765432 The PaymentIntent status transitions from processing to succeeded.
    GB55WEST12345698765433 The PaymentIntent status transitions from processing to requires_payment_method due to payment failure.
    GB28WEST12345698765434 The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.

    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?

    Feedback about this page?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    On this page