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.
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:
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.
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.
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.
# Set your secret key. Remember to switch to your live secret key in production.# See your keys here: https://dashboard.stripe.com/apikeysStripe.api_key =
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.
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.
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
<formaction="/form"method="post"id="setup-form"><divclass="form-row inline"><divclass="col"><labelfor="accountholder-name">
Name
</label><inputid="accountholder-name"name="accountholder-name"placeholder="Jenny Rosen"required/></div><divclass="col"><labelfor="email">
Email Address
</label><inputid="email"name="email"type="email"placeholder="jenny.rosen@example.com"required/></div></div><divclass="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.
--><labelfor="iban-element">
IBAN
</label><divid="iban-element"><!-- A Stripe Element will be inserted here. --></div></div><!-- Add the client_secret from the SetupIntent as a data attribute --><buttonid="submit-button"data-secret="{CLIENT_SECRET}">
Set up SEPA Direct Debit
</button><!-- Display mandate acceptance text. --><divid="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. --><divid="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 Elementconst iban = elements.create('iban', options);// Add an instance of the IBAN Element into the `iban-element` <div>
iban.mount('#iban-element');
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 keyvar stripe =Stripe('pk_test_1234');// Get the IBAN information from your elementvar 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 subscriptionconst res =awaitfetch('/create-subscription',{
method:'POST',});const{type, clientSecret}=await res.json();const confirmIntent = type ==='setup'? stripe.confirmSepaDebitSetup: stripe.confirmSepaDebitPayment;const{error}=awaitconfirmIntent({
clientSecret,{
payment_method:{
sepa_debit: iban,
billing_details:{
name: accountholderName.value,
email: email.value,},},}});});
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:
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 Number
Description
AT611904300234573201
The PaymentIntent status transitions from processing to succeeded.
AT321904300235473204
The PaymentIntent status transitions from processing to succeeded after at least three minutes.
AT861904300235473202
The PaymentIntent status transitions from processing to requires_payment_method.
AT051904300235473205
The PaymentIntent status transitions from processing to requires_payment_method after at least three minutes.
AT591904300235473203
The PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.
AT981904300000343434
The payment fails with a charge_exceeds_source_limit failure code due to payment amount causing account to exceed its weekly payment volume limit.
AT601904300000121212
The payment fails with a charge_exceeds_weekly_limit failure code due to payment amount exceeding account's transaction volume limit.
AT981904300002222227
The payment fails with an insufficient_funds failure code.