Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Revenue
Platforms and marketplaces
Money management
Developer tools
Overview
Billing
    Overview
    About the Billing APIs
    Subscriptions
      How subscriptions work
      Quickstart
      Use cases
      Build your integration
      Subscription features
        Subscription invoices
        Subscription schedules
        Subscription pricing
        Recurring pricing models
        Embed a pricing table
        Start subscriptions
        Set quantities
        Set billing cycles
        Backdate subscriptions
        Subscribe to multiple items
        Set trial periods
        Apply coupons
        Migrate subscriptions to Stripe
        How credit prorations are calculated
        Subscription payments
        Subscription payment methods
          ACH Direct Debit
          Amazon Pay
          Bacs Direct Debit in the UK
          Bank transfer
          BECS Direct Debit in Australia
          Cash App Pay
          PayPal
          Revolut Pay
          Korean Cards
          Kakao Pay
          Naver Pay
          Pre-authorized debit in Canada
          SEPA Direct Debit in the EU
          iDEAL with SEPA Direct Debit
          Bancontact with SEPA Direct Debit
        Integrate with third-party payment processing
        Collection methods
        Share a link to update payment details
        Strong Customer Authentication (SCA)
        Manage subscriptions
        Modify subscriptions
        Manage pending updates
      Analytics
    Invoicing
    Usage-based billing
    Connect and Billing
    Tax and Billing
    Quotes
    Revenue recovery
    Automations
    Scripts
    Revenue recognition
    Customer management
    Entitlements
    Test your integration
Tax
Reporting
Data
Startup incorporation
HomeRevenueBillingSubscriptionsSubscription featuresSubscription payment methods

Set up a subscription with SEPA Direct Debit

Learn how to create and charge a subscription with SEPA Direct Debit.

Copy page

Learn how to create and charge for a subscription with SEPA Direct Debit.

Note

If you’re a new user, use the Payment Element instead of using Stripe Elements as described in this guide. The Payment Element provides a low-code integration path with built-in conversion optimizations. For instructions, see Build a subscription.

Create a product and price
Dashboard

Products represent the item or service you’re selling. Prices define how much and how frequently you charge for a product. This includes how much the product costs, what currency you accept, and whether it’s a one-time or recurring charge. If you only have a few products and prices, create and manage them in the Dashboard.

This guide uses a stock photo service as an example and charges customers a 15 EUR monthly subscription. To model this:

  1. Navigate to the Add a product page.
  2. Enter a Name for the product.
  3. Enter 15 for the price.
  4. Select EUR as the currency.
  5. Click Save product.

After you create the product and the price, record the price ID so you can use it in subsequent steps. The pricing page displays the ID and it looks similar to this: price_G0FvDp6vZvdwRZ.

Create a customer
Server-side

A subscription needs a customer so it can reuse payment methods and track recurring payments. Create a Customer object when your customer creates an account with your business.

Command Line
cURL
curl -X POST https://api.stripe.com/v1/customers \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"

Create the subscription
Server-side

Create the subscription using the customer and price IDs. Return to the client side the client_secret from either the latest invoice’s confirmation_secret.client_secret or, for subscriptions that don’t collect a payment up front, the pending_setup_intent. Additionally, set:

  • payment_behavior to default_incomplete to simplify collection of the SEPA Direct Debit mandate.
  • save_default_payment_method to on_subscription to save the payment method as the default for the subscription when the payment succeeds. Saving a default payment method increases the success rate of future subscription payments.
server.rb
Ruby
# Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key =
'sk_test_BQokikJOvBiI2HlWgH4olfQ2'
post '/create-subscription' do content_type 'application/json' data = JSON.parse(request.body.read) customer_id = cookies[:customer] price_id = data['priceId'] subscription = Stripe::Subscription.create( customer: customer_id, items: [{ price: price_id, }], payment_behavior: 'default_incomplete', payment_settings: {save_default_payment_method: 'on_subscription'}, expand: ['latest_invoice.confirmation_secret', 'pending_setup_intent'] ) if subscription.pending_setup_intent != nil { type: 'setup', clientSecret: subscription.pending_setup_intent.client_secret }.to_json else { type: 'payment', clientSecret: subscription.latest_invoice.confirmation_secret.client_secret }.to_json end end

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

payment_setup.html
<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:

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 (containers) 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 the mandate.

Replace Rocket Rides with your company name.

Authorization text template

By providing your payment information and confirming this payment, you authorise (A) 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.

Copy

​​Setting up a payment method creates the accepted mandate. As the customer has implicitly signed the mandate when accepting these terms, you must communicate these terms in your form or through email.

payment_setup.html
<form action="/form" method="post" id="setup-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 SetupIntent as a data attribute --> <button id="submit-button" data-secret="{CLIENT_SECRET}"> Set up SEPA Direct Debit </button> <!-- Display mandate acceptance text. --> <div id="mandate-acceptance"> By providing your payment information and confirming this payment, you authorise (A) Rocket Rides and Stripe, our payment service provider and/or PPRO, its local 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>

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. 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');

Submit the payment method details to Stripe
Client-side

Use confirmSepaDebitPayment or, for subscriptions that don’t collect a payment up front, confirmSepaDebitSetup to confirm the subscription and create a SEPA Direct Debit PaymentMethod. Include the customer’s name and email address in the payment_method.billing_details properties.

PaymentSetup.js
// Define stripe with your publishable key var stripe = Stripe('pk_test_1234'); // Get the IBAN information from your element var iban = document.getElementById('iban-element'); const form = document.getElementById('payment-form'); const accountholderName = document.getElementById('accountholder-name'); const email = document.getElementById('email'); form.addEventListener('submit', async(event) => { event.preventDefault(); // Create the subscription const res = await fetch('/create-subscription', { method: 'POST', }); const {type, clientSecret} = await res.json(); const confirmIntent = type === 'setup' ? stripe.confirmSepaDebitSetup : stripe.confirmSepaDebitPayment; const {error} = await confirmIntent({ clientSecret, { payment_method: { sepa_debit: iban, billing_details: { name: accountholderName.value, email: email.value, }, }, } }); });

Set the default payment method
Server-side

You need to add a stored payment method to the customer so future payments are successful. You do this by setting the payment method you just collected at the top level of the Customer object and as the default payment method for invoices:

Command Line
cURL
curl https://api.stripe.com/v1/customers/cus_Gk0uVzT2M4xOKD \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -d "invoice_settings[default_payment_method]"=pm_1F0c9v2eZvKYlo2CJDeTrB4n

Manage subscription status
Client-side

Assuming the initial payment succeeds, the state of the subscription is active and no further action is needed. When payments fail, the status is changed to the Subscription status configured in your automatic collection settings. You should notify the customer on failure and charge them with a different payment method.

Note

SEPA Direct Debit payments are never automatically retried, even if you have a retry schedule configured for other payment methods.

Test the integration

You can test your integration using the IBANs below. The payment method details are successfully collected for each IBAN but exhibit different behavior when charged.

Test IBANs
Account NumberDescription
AT611904300234573201The PaymentIntent status transitions from processing to succeeded.
AT321904300235473204The PaymentIntent status transitions from processing to succeeded after at least three minutes.
AT861904300235473202The PaymentIntent status transitions from processing to requires_payment_method.
AT051904300235473205The PaymentIntent status transitions from processing to requires_payment_method after at least three minutes.
AT591904300235473203The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
AT981904300000343434The payment fails with a charge_exceeds_source_limit failure code due to payment amount causing account to exceed its weekly payment volume limit.
AT601904300000121212The payment fails with a charge_exceeds_weekly_limit failure code due to payment amount exceeding account's transaction volume limit.
AT981904300002222227The payment fails with an insufficient_funds failure code.

OptionalSetting the billing cycle

OptionalSubscription trials

OptionalCreate SEPA Direct Debit payments using other payment methods

Was this page helpful?
YesNo
Need help? Contact Support.
Join our early access program.
Check out our changelog.
Questions? Contact Sales.
LLM? Read llms.txt.
Powered by Markdoc