Payments
Bank redirects
iDEAL
Set up future payments

Use iDEAL to set up future SEPA Direct Debit payments

Learn how to save bank details from an iDEAL payment and charge your customers later with SEPA Direct Debit.

The Setup Intents API lets you save a customer’s payment method details for future payments. This is helpful if you want to onboard customers now, set them up for payments, and charge them in the future—when they’re offline.

iDEAL is a single use payment method where customers are required to authenticate each payment. When you use the Setup Intents API with iDEAL, Stripe will charge your customer 0.01 EUR through iDEAL in order to collect their bank details. After your customer authenticates the payment, Stripe will refund the payment and store your customer’s IBAN in a SEPA Direct Debit Payment Method object. You can proceed to use the generated SEPA Direct Debit Payment Method to accept payments.

Setting up future SEPA Direct Debit payments using iDEAL on your website consists of creating a SetupIntent object to track the process, collecting mandate acknowledgement, and redirecting your customer to iDEAL. Stripe uses the SetupIntent to track and handle all the states of the setup until the setup 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 use a SEPA Direct Debit PaymentMethod 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.

When you include customer in your SetupIntent before confirming, Stripe automatically attaches the generated SEPA Direct Debit Payment Method to the provided Customer object.

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 SetupIntent Server-side

A SetupIntent is an object that represents your intent to set up a customer’s payment method for future payments. The SetupIntent will track the steps of this set-up process. For iDEAL, this includes collecting a SEPA Direct Debit mandate from the customer and tracking its validity throughout its lifecycle.

Create a SetupIntent on your server with payment_method_types set to ideal and specify the Customer’s id.

curl https://api.stripe.com/v1/setup_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=ideal \ -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' setup_intent = Stripe::SetupIntent.create({ payment_method_types: ['ideal'], customer: customer['id'], }) end
# 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' setup_intent = stripe.SetupIntent.create( payment_method_types=['ideal'], 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'); $setup_intent = \\Stripe\\SetupIntent::create([ 'payment_method_types' => ['ideal'], '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"; Map<String, Object> params = new HashMap<>(); List<Object> paymentMethodTypes = new ArrayList<>(); paymentMethodTypes.add("ideal"); params.put("payment_method_types", paymentMethodTypes); params.put("customer", customer.getId()); SetupIntent setupIntent = SetupIntent.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 setupIntent = await stripe.setupIntents.create({ payment_method_types: ['ideal'], 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.SetupIntentParams{ PaymentMethodTypes: stripe.StringSlice([]string{"ideal"}), Customer: stripe.String(c.ID), } si, _ := setupintent.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 SetupIntentCreateOptions { PaymentMethodTypes = new List<string> { "ideal" }, Customer = customer.Id, }; var service = new SetupIntentService(); service.Create(options);

4 Collect payment method details and mandate acknowledgement 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.

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

Create an instance of Elements with the following JavaScript on your checkout 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 idealBank Element

Elements needs a place to live in your payment form. Create empty DOM nodes (containers) with unique IDs in your payment form and then pass those IDs to Elements.

To process SEPA Direct Debit payments in the future, you must collect mandate agreement from your customer now. 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 payment method. Because the customer has implicitly signed the mandate when accepting the terms suggested above, you must communicate the terms on the form or in an email.

<form id="setup-form"> <div class="form-row"> <label for="accountholder-name"> Name </label> <input id="accountholder-name" name="accountholder-name"> </div> <div class="form-row"> <label for="email"> Email </label> <input id="email" name="email"> </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="ideal-bank-element"> iDEAL Bank </label> <div id="ideal-bank-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}">Submit</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 `idealBank` element. * https://stripe.com/docs/js/elements_object/create_element?type=idealBank#elements_create-options-classes */ input, .StripeElement { height: 40px; 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 { padding: 10px 12px; } input:focus, .StripeElement--focus { box-shadow: 0 1px 3px 0 #cfd7df; }

When the form above has loaded, create an instance of an idealBank Element and mount it to the Element container created above:

var options = { // Custom styling can be passed to options when creating an Element style: { base: { padding: '10px 12px', color: '#32325d', fontSize: '16px', '::placeholder': { color: '#aab7c4' }, }, }, }; // Create an instance of the idealBank Element var idealBank = elements.create('idealBank', options); // Add an instance of the idealBank Element into // the `ideal-bank-element` <div> idealBank.mount('#ideal-bank-element');
const options = { // Custom styling can be passed to options when creating an Element style: { base: { padding: '10px 12px', color: '#32325d', fontSize: '16px', '::placeholder': { color: '#aab7c4' }, }, }, }; // Create an instance of the idealBank Element const idealBank = elements.create('idealBank', options); // Add an instance of the idealBank Element into // the `ideal-bank-element` <div> idealBank.mount('#ideal-bank-element');

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.

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 IdealBankElement component

Use the IdealBankElement to allow your customer to select their preferred bank.

/** * Use the CSS tab above to style your Element's container. */ import React from 'react'; import {IdealBankElement} from '@stripe/react-stripe-js'; import './IdealBankSectionStyles.css' const IDEAL_ELEMENT_OPTIONS = { // Custom styling can be passed to options when creating an Element style: { base: { padding: '10px 12px', color: '#32325d', fontSize: '16px', '::placeholder': { color: '#aab7c4' }, }, }, }; function IdealBankSection() { return ( <label> iDEAL Bank <IdealBankElement options={IDEAL_ELEMENT_OPTIONS} /> </label> ); }; export default IdealBankSection;
/** * 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 IdealBankElement component. * https://stripe.com/docs/js/elements_object/create_element?type=idealBank#elements_create-options-classes */ input, .StripeElement { height: 40px; 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 { padding: 10px 12px; } input:focus, .StripeElement--focus { box-shadow: 0 1px 3px 0 #cfd7df; }

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 method details to Stripe Client-side

Rather than sending the entire SetupIntent 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 setup. Do not log it, embed it in URLs, or expose it to anyone but the customer.

Use stripe.confirmIdealSetup to complete the setup 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.

A successful setup will return a succeeded value for the SetupIntent’s status property. If the setup is not successful, inspect the returned error to determine the cause.

Because customer was set, the PaymentMethod will be attached to the provided Customer object after a successful setup. This allows you to use the stored PaymentMethod to collect future payments without prompting the customer for payment method details.

var form = document.getElementById('setup-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.confirmIdealSetup( clientSecret, { payment_method: { ideal: idealBank, billing_details: { name: accountholderName.value, email: email.value, }, }, return_url: 'https://your-website.com/checkout/complete', } ); });
const form = document.getElementById('setup-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.confirmIdealSetup( clientSecret, { payment_method: { ideal: idealBank, billing_details: { name: accountholderName.value, email: email.value, }, }, return_url: 'https://your-website.com/checkout/complete', } ); });

The return_url should correspond to a page on your website that displays the result of the setup. You can determine what to display by verifying the status of the SetupIntent. When Stripe redirects the customer to the return_url, the following URL query parameters are provided to verify status. You may also append your own query parameters when providing the return_url. They will persist through the redirect process.

Parameter Description
setup_intent The unique identifier for the SetupIntent
setup_intent_client_secret The client secret of the SetupIntent object

To process SEPA Direct Debit payments in the future, you must collect mandate agreement from your customer now. 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. Because the customer has implicitly signed the mandate when accepting the terms suggested above, you must communicate the terms on the form or in an email.

Use stripe.confirmIdealSetup to handle the redirect away from your page and to complete the setup. Add a return_url to this function to indicate where Stripe should redirect the user after they complete the setup on their bank’s website or mobile application.

To call stripe.confirmIdealSetup from your setup 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, IdealBankElement} from '@stripe/react-stripe-js'; import IdealBankSection from './IdealBankSection'; 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 idealBank = elements.getElement(IdealBankElement); // For brevity, this example is using uncontrolled components for // the accountholder's name. 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 accountholderEmail = event.target['accountholder-email']; const {error} = await stripe.confirmIdealSetup('{CLIENT_SECRET}', { payment_method: { ideal: idealBank, billing_details: { name: accountholderName.value, email: accountholderEmail.value, }, }, return_url: 'https://your-website.com/checkout/complete', }); if (error) { // Show error to your customer. console.log(error.message); } // Otherwise the customer will be redirected away from your // page to complete the setup with their bank. }; return ( <form onSubmit={handleSubmit}> <div className="form-row"> <label> Name <input name="accountholder-name" placeholder="Jenny Rosen" required /> </label> </div> <div className="form-row"> <label> Email <input name="accountholder-email" placeholder="jenny.rosen@example.com" required /> </label> </div> <div className="form-row"> <IdealBankSection /> </div> <button type="submit" disabled={!stripe}> Submit </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> </form> ); }
import React from 'react'; import {ElementsConsumer, IdealBankElement} from '@stripe/react-stripe-js'; import IdealBankSection from './IdealBankSection'; 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 idealBank = elements.getElement(IdealBankElement); // For brevity, this example is using uncontrolled components for // the accountholder's name. 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 accountholderEmail = event.target['accountholder-email']; const {error} = await stripe.confirmIdealSetup('{CLIENT_SECRET}', { payment_method: { ideal: idealBank, billing_details: { name: accountholderName.value, email: accountholderEmail.value, }, }, return_url: 'https://your-website.com/checkout/complete', }); if (error) { // Show error to your customer. console.log(error.message); } // Otherwise the customer will be redirected away from your // page to complete the setup with their bank. }; render() { const {stripe} = this.props; return ( <form onSubmit={this.handleSubmit}> <div className="form-row"> <label> Name <input name="accountholder-name" placeholder="Jenny Rosen" required /> </label> </div> <div className="form-row"> <label> Email <input name="accountholder-email" placeholder="jenny.rosen@example.com" required /> </label> </div> <div className="form-row"> <IdealBankSection /> </div> <button type="submit" disabled={!stripe}> Submit </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> </form> ); } } export default function InjectedCheckoutForm() { return ( <ElementsConsumer> {({stripe, elements}) => ( <CheckoutForm stripe={stripe} elements={elements} /> )} </ElementsConsumer> ); }

6 Charge the SEPA Direct Debit PaymentMethod later Server-side

When you are ready to charge your customer, use the Customer and SEPA Direct Debit PaymentMethod IDs to create a new PaymentIntent.

You can find the ID of the generated SEPA Direct Debit PaymentMethod by listing the PaymentMethods associated with your Customer.

curl https://api.stripe.com/v1/payment_methods \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d customer="{{CUSTOMER_ID}}" \ -d type=sepa_debit
Stripe::PaymentMethod.list({ customer: '{{CUSTOMER_ID}}', type: 'sepa_debit', })
stripe.PaymentMethod.list( customer="{{CUSTOMER_ID}}", type="sepa_debit", )
$stripe->paymentMethods->all([ 'customer' => '{{CUSTOMER_ID}}', 'type' => 'sepa_debit', ]);
Map<String, Object> params = new HashMap<>(); params.put("customer", "{{CUSTOMER_ID}}"); params.put("type", "sepa_debit"); PaymentMethodCollection paymentMethods = PaymentMethod.list(params);
stripe.paymentMethods.list( {customer: '{{CUSTOMER_ID}}', type: 'sepa_debit'}, function(err, paymentMethods) { // asynchronously called } );
params := &stripe.PaymentMethodListParams{ Customer: stripe.String("{{CUSTOMER_ID}}"), Type: stripe.String("sepa_debit"), } i := paymentmethod.List(params) for i.Next() { pm := i.PaymentMethod() }
var options = new PaymentMethodListOptions { Customer = "{{CUSTOMER_ID}}", Type = "sepa_debit", }; var service = new PaymentMethodService(); StripeList<PaymentMethod> paymentMethods = service.List( options );

Next, create a PaymentIntent with payment_method set to the ID of the SEPA Direct Debit PaymentMethod and customer set to the ID of the Customer.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=sepa_debit \ -d amount=1099 \ -d currency=eur \ -d customer="{{CUSTOMER_ID}}" \ -d payment_method="{{SEPA_DEBIT_PAYMENT_METHOD_ID}}" \ -d confirm=true
# 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({ payment_method_types: ['sepa_debit'], amount: 1099, currency: 'eur', customer: '{{CUSTOMER_ID}}', payment_method: '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm: true, })
# 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( payment_method_types=['sepa_debit'], amount=1099, currency='eur', customer='{{CUSTOMER_ID}}', payment_method='{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm=True, )
// 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([ 'payment_method_types' => ['sepa_debit'], 'amount' => 1099, 'currency' => 'eur', 'customer' => '{{CUSTOMER_ID}}', 'payment_method' => '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', 'confirm' => true, ]);
// 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") .setCustomer("{{CUSTOMER_ID}}") .setPaymentMethod("{{SEPA_DEBIT_PAYMENT_METHOD_ID}}") .addPaymentMethodType("sepa_debit") .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 paymentIntent = await stripe.paymentIntents.create({ payment_method_types: ['sepa_debit'], amount: 1099, currency: 'eur', customer: '{{CUSTOMER_ID}}', payment_method: '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm: true, });
// 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))), Customer: stripe.String("{{CUSTOMER_ID}}"), PaymentMethod: stripe.String("{{SEPA_DEBIT_PAYMENT_METHOD_ID}}"), PaymentMethodTypes: stripe.StringSlice([]string{ "sepa_debit", }), } 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", Customer = "{{CUSTOMER_ID}}", PaymentMethod = "{{SEPA_DEBIT_PAYMENT_METHOD_ID}}", PaymentMethodTypes = new List<string> { "sepa_debit", }, }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options);

For more information about testing your SEPA Direct Debit integration, see our full guide on accepting payments with SEPA Direct Debit.

Additional information about the successful setup can be found by retrieving the SetupIntent and expanding the latest_attempt field.

curl https://api.stripe.com/v1/setup_intents/{{SETUP_INTENT_ID}} \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "expand[]"=latest_attempt
# 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::SetupIntent.retrieve({ id: '{{SETUP_INTENT_ID}}', expand: ['latest_attempt'], })
# 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.SetupIntent.retrieve( '{{SETUP_INTENT_ID}}', expand=['latest_attempt'] )
// 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->setupIntents->retrieve([ 'id' => '{{SETUP_INTENT_ID}}', 'expand' => ['latest_attempt'], ]);
// 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"; List<String> expandList = new ArrayList<>(); expandList.add("latest_attempt"); Map<String, Object> params = new HashMap<>(); params.put("expand", expandList); SetupIntent setupIntent = SetupIntent.retrieve("{{SETUP_INTENT_ID}}", params, null);
// 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'); stripe.setupIntents.retrieve('{{SETUP_INTENT_ID}}', { expand: ['latest_attempt'], });
// 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.SetupIntentParams{} params.AddExpand("latest_attempt") si, _ := setupintent.Get("{{SETUP_INTENT_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 service = new SetupIntentService(); var options = new SetupIntentGetOptions(); options.AddExpand("latest_attempt"); var service = new SetupIntentService(); service.Get("{{SETUP_INTENT_ID}}", options);

7 Test your integration

Select any bank in the iDEAL bank list with your test API keys. After confirming the SetupIntent, you’re redirected to a test page with options to succeed or fail the payment method setup. You can test the successful case by authenticating the payment on the redirect page. The SetupIntent will transition from requires_action to succeeded.

To test the case where the user fails to authenticate, select any bank with your test API keys. On the redirect page, click Fail test payment. The SetupIntent will transition from requires_action to requires_payment_method.

Test your SEPA Direct Debit integration

When you create an iDEAL PaymentMethod with your test API keys, you can configure it to store different test account numbers in the generated SEPA Direct Debit PaymentMethod. These account numbers will not change the outcome of the initial iDEAL SetupIntent. PaymentIntents confirmed with the generated SEPA Direct Debit PaymentMethod will follow the behavior described below.

When confirming your iDEAL SetupIntent in test mode, set payment_method.billing_details.email to one of the following values to change the behavior of the generated SEPA Direct Debit PaymentMethod.

You can optionally include your own custom text at the beginning of the email address followed by an underscore. For example, "test_1_generatedSepaDebitIntentsFail@example.com" will result in a SEPA Direct Debit PaymentMethod that will always fail when used in a PaymentIntent.

Email Address Description
generatedSepaDebitIntentsSucceed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
generatedSepaDebitIntentsSucceedDelayed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
generatedSepaDebitIntentsFail@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
generatedSepaDebitIntentsFailDelayed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
generatedSepaDebitIntentsSucceedDisputed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.

Instead of creating a PaymentMethod, you can use an existing test iDEAL PaymentMethod to change the generated SEPA Direct Debit PaymentMethod’s behavior. These PaymentMethod tokens can be useful for automated testing if you want to immediately attach the PaymentMethod to the Sofort SetupIntent on the server.

Payment Method Description
pm_ideal_generatedSepaDebitIntentsSucceed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
pm_ideal_generatedSepaDebitIntentsSucceedDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
pm_ideal_generatedSepaDebitIntentsFail The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
pm_ideal_generatedSepaDebitIntentsFailDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
pm_ideal_generatedSepaDebitIntentsSucceedDisputed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.

Optional Handle post-setup events

Stripe sends a setup_intent.succeeded event when the SetupIntent completes. Use the Dashboard, a custom webhook, or a partner solution to receive these events and run actions, like saving the generated SEPA Direct Debit PaymentMethod to your database or charging your customer after a trial period.

Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes. Setting up your integration to listen for asynchronous events also makes it easier to accept more payment methods in the future. Check out our guide to payment methods to see the differences between all supported payment methods.

Receive events and run business actions

Manually

Use the Stripe Dashboard to view all your Stripe payments, send email receipts, handle payouts, or retry failed payments.

Custom code

Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.

Prebuilt apps

Handle common business events, like shipping and inventory management, by integrating a partner application.

Optional Handle iDEAL Bank Element changes

The iDEAL Bank Element outputs the customer’s selected bank as it changes. To perform additional logic with the bank value (e.g., requiring the field for form validation), you can listen to the change event:

idealBank.on('change', function(event) { var bank = event.value; // Perform any additional logic here... });
idealBank.on('change', function(event) { const bank = event.value; // Perform any additional logic here... });
<IdealBankElement onChange={(event) => { const bank = event.value; // Perform any additional logic here... }}>

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

Setting up future SEPA Direct Debit payments using iDEAL in your app consists of creating a SetupIntent object to track the process, collecting mandate acknowledgement, and redirecting your customer to iDEAL. Stripe uses the SetupIntent to track and handle all the states of the setup until the setup completes.

1 Set up Stripe Server-side Client-side

First, you need a Stripe account. Register now.

Server-side

This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

# 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

Client-side

The iOS SDK is open source, fully documented, and compatible with apps supporting iOS 11 or above.

  1. If you haven't already, install the latest version of CocoaPods.
  2. If you don't have an existing Podfile, run the following command to create one:
    pod init
  3. Add this line to your Podfile:
    pod 'Stripe'
  4. Run the following command:
    pod install
  5. Don't forget to use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file, from here on out.
  6. In the future, to update to the latest version of the SDK, just run:
    pod update Stripe
  1. If you haven't already, install the latest version of Carthage.
  2. Add this line to your Cartfile:
    github "stripe/stripe-ios"
  3. Follow the Carthage installation instructions.
  4. In the future, to update to the latest version of the SDK, run the following command:
    carthage update stripe-ios --platform ios
  1. Head to our GitHub releases page and download and unzip Stripe.framework.zip.
  2. Drag Stripe.framework to the "Embedded Binaries" section of your Xcode project's "General" settings. Make sure to select "Copy items if needed".
  3. Head to the "Build Phases" section of your Xcode project settings, and create a new "Run Script Build Phase". Paste the following snippet into the text field:
    bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Stripe.framework/integrate-dynamic-framework.sh"
  4. In the future, to update to the latest version of our SDK, just repeat steps 1 and 2.

When your app starts, configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API.

import UIKit import Stripe @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { StripeAPI.defaultPublishableKey = "pk_test_TYooMQauvdEDq54NiTphI7jx" // do any other necessary launch configuration return true } }
#import "AppDelegate.h" @import Stripe; @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [StripeAPI setDefaultPublishableKey:@"pk_test_TYooMQauvdEDq54NiTphI7jx"]; // do any other necessary launch configuration return YES; } @end

2 Create or retrieve a Customer Server-side

To use a SEPA Direct Debit PaymentMethod 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.

When you include customer in your SetupIntent before confirming, Stripe automatically attaches the generated SEPA Direct Debit Payment Method to the provided Customer object.

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 SetupIntent Server-side

A SetupIntent is an object that represents your intent to set up a customer’s payment method for future payments. The SetupIntent will track the steps of this set-up process. For iDEAL, this includes collecting a SEPA Direct Debit mandate from the customer and tracking its validity throughout its lifecycle.

Create a SetupIntent on your server with payment_method_types set to ideal and specify the Customer’s id.

curl https://api.stripe.com/v1/setup_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=ideal \ -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' setup_intent = Stripe::SetupIntent.create({ payment_method_types: ['ideal'], customer: customer['id'], }) end
# 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' setup_intent = stripe.SetupIntent.create( payment_method_types=['ideal'], 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'); $setup_intent = \\Stripe\\SetupIntent::create([ 'payment_method_types' => ['ideal'], '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"; Map<String, Object> params = new HashMap<>(); List<Object> paymentMethodTypes = new ArrayList<>(); paymentMethodTypes.add("ideal"); params.put("payment_method_types", paymentMethodTypes); params.put("customer", customer.getId()); SetupIntent setupIntent = SetupIntent.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 setupIntent = await stripe.setupIntents.create({ payment_method_types: ['ideal'], 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.SetupIntentParams{ PaymentMethodTypes: stripe.StringSlice([]string{"ideal"}), Customer: stripe.String(c.ID), } si, _ := setupintent.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 SetupIntentCreateOptions { PaymentMethodTypes = new List<string> { "ideal" }, Customer = customer.Id, }; var service = new SetupIntentService(); service.Create(options);

4 Collect payment method details and mandate acknowledgement Client-side

In your app, collect your customer’s full name, email address, and the name of their bank (e.g., abn_amro). Create an STPPaymentMethodParams object with these details.

let iDEALParams = STPPaymentMethodiDEALParams() iDEALParams.bankName = "abn_amro" let billingDetails = STPPaymentMethodBillingDetails() billingDetails.name = "Jane Doe" billingDetails.email = "jane.doe@example.com" let paymentMethodParams = STPPaymentMethodParams(iDEAL: iDEALParams, billingDetails: billingDetails, metadata: nil)
STPPaymentMethodiDEALParams *iDEALParams = [[STPPaymentMethodiDEALParams alloc] init]; iDEALParams.bankName = @"abn_amro"; STPPaymentMethodBillingDetails *billingDetails = [[STPPaymentMethodBillingDetails alloc] init]; billingDetails.name = @"Jane Doe"; billingDetails.email = @"jane.doe@example.com"; STPPaymentMethodParams *paymentMethodParams = [STPPaymentMethodParams paramsWithiDEAL:iDEALParams billingDetails:billingDetails metadata:nil];

To process SEPA Direct Debit payments in the future, you must collect mandate agreement from your customer now. 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 payment method. Because the customer has implicitly signed the mandate when accepting the terms suggested above, you must communicate the terms on the form or in an email.

5 Submit the payment method details to Stripe Client-side

Retrieve the client secret from the SetupIntent you created in step 3 and call STPPaymentHandler confirmSetupIntent. This presents a webview where the customer can complete the payment on their bank’s website or app. Afterwards, the completion block is called with the result of the payment.

let setupIntentParams = STPSetupIntentConfirmParams(clientSecret: paymentIntentClientSecret) setupIntentParams.paymentMethodParams = paymentMethodParams STPPaymentHandler.shared().confirmSetupIntent(withParams: setupIntentParams, authenticationContext: self) { (handlerStatus, setupIntent, error) in switch handlerStatus { case .succeeded: // Setup succeeded case .canceled: // Setup was cancelled case .failed: // Setup failed @unknown default: fatalError() } }
STPSetupIntentConfirmParams *setupIntentParams = [[STPSetupIntentConfirmParams alloc] initWithClientSecret:paymentIntentClientSecret]; setupIntentParams.paymentMethodParams = paymentMethodParams; [[STPPaymentHandler sharedHandler] confirmSetupIntent:setupIntentParams withAuthenticationContext:self completion:^(STPPaymentHandlerActionStatus handlerStatus, STPSetupIntent * handledIntent, NSError * _Nullable handlerError) { switch (handlerStatus) { case STPPaymentHandlerActionStatusFailed: // Setup failed break; case STPPaymentHandlerActionStatusCanceled: // Setup was cancelled break; case STPPaymentHandlerActionStatusSucceeded: // Setup succeeded break; } }];

6 Charge the SEPA Direct Debit PaymentMethod later

When you are ready to charge your customer, use the Customer and SEPA Direct Debit PaymentMethod IDs to create a new PaymentIntent.

You can find the ID of the generated SEPA Direct Debit PaymentMethod by listing the PaymentMethods associated with your Customer.

curl https://api.stripe.com/v1/payment_methods \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d customer="{{CUSTOMER_ID}}" \ -d type=sepa_debit
Stripe::PaymentMethod.list({ customer: '{{CUSTOMER_ID}}', type: 'sepa_debit', })
stripe.PaymentMethod.list( customer="{{CUSTOMER_ID}}", type="sepa_debit", )
$stripe->paymentMethods->all([ 'customer' => '{{CUSTOMER_ID}}', 'type' => 'sepa_debit', ]);
Map<String, Object> params = new HashMap<>(); params.put("customer", "{{CUSTOMER_ID}}"); params.put("type", "sepa_debit"); PaymentMethodCollection paymentMethods = PaymentMethod.list(params);
stripe.paymentMethods.list( {customer: '{{CUSTOMER_ID}}', type: 'sepa_debit'}, function(err, paymentMethods) { // asynchronously called } );
params := &stripe.PaymentMethodListParams{ Customer: stripe.String("{{CUSTOMER_ID}}"), Type: stripe.String("sepa_debit"), } i := paymentmethod.List(params) for i.Next() { pm := i.PaymentMethod() }
var options = new PaymentMethodListOptions { Customer = "{{CUSTOMER_ID}}", Type = "sepa_debit", }; var service = new PaymentMethodService(); StripeList<PaymentMethod> paymentMethods = service.List( options );

Next, create a PaymentIntent with payment_method set to the ID of the SEPA Direct Debit PaymentMethod and customer set to the ID of the Customer.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=sepa_debit \ -d amount=1099 \ -d currency=eur \ -d customer="{{CUSTOMER_ID}}" \ -d payment_method="{{SEPA_DEBIT_PAYMENT_METHOD_ID}}" \ -d confirm=true
# 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({ payment_method_types: ['sepa_debit'], amount: 1099, currency: 'eur', customer: '{{CUSTOMER_ID}}', payment_method: '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm: true, })
# 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( payment_method_types=['sepa_debit'], amount=1099, currency='eur', customer='{{CUSTOMER_ID}}', payment_method='{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm=True, )
// 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([ 'payment_method_types' => ['sepa_debit'], 'amount' => 1099, 'currency' => 'eur', 'customer' => '{{CUSTOMER_ID}}', 'payment_method' => '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', 'confirm' => true, ]);
// 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") .setCustomer("{{CUSTOMER_ID}}") .setPaymentMethod("{{SEPA_DEBIT_PAYMENT_METHOD_ID}}") .addPaymentMethodType("sepa_debit") .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 paymentIntent = await stripe.paymentIntents.create({ payment_method_types: ['sepa_debit'], amount: 1099, currency: 'eur', customer: '{{CUSTOMER_ID}}', payment_method: '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm: true, });
// 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))), Customer: stripe.String("{{CUSTOMER_ID}}"), PaymentMethod: stripe.String("{{SEPA_DEBIT_PAYMENT_METHOD_ID}}"), PaymentMethodTypes: stripe.StringSlice([]string{ "sepa_debit", }), } 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", Customer = "{{CUSTOMER_ID}}", PaymentMethod = "{{SEPA_DEBIT_PAYMENT_METHOD_ID}}", PaymentMethodTypes = new List<string> { "sepa_debit", }, }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options);

For more information about testing your SEPA Direct Debit integration, see our full guide on accepting payments with SEPA Direct Debit.

Additional information about the iDEAL payment can be found on the iDEAL PaymentIntent’s successful Charge, under the payment_method_details property.

{ "charges": { "data": [ { "payment_method_details": { "ideal": { "bank": "ing", "bic": "INGBNL2A", "iban_last4": "****", "generated_sepa_debit": "pm_1GrddXGf98efjktuBIi3ag7aJQ", "verified_name": "JENNY ROSEN" }, "type": "ideal" }, "id": "src_16xhynE8WzK49JbAs9M21jaR",
See all 49 lines "object": "source", "amount": 1099, "client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU", "created": 1445277809, "currency": "eur", "flow": "redirect", "livemode": true, "statement_descriptor": null, "status": "pending", "type": "ideal", "usage": "single_use" } ], "object": "list", "has_more": false, "url": "/v1/charges?payment_intent=pi_1G1sgdKi6xqXeNtkldRRE6HT" }, "payment_method_options": { "ideal": {} }, "payment_method_types": [ "ideal" ], "id": "pi_1G1sgdKi6xqXeNtkldRRE6HT", "object": "payment_intent", "amount": 1099, "client_secret": "pi_1G1sgdKi6xqXeNtkldRRE6HT_secret_h9B56ObhTN72fQiBAuzcVPb2E", "confirmation_method": "automatic", "created": 1579259303, "currency": "eur", "customer": "cus_f0Us034jfkXcl0CJQ", "livemode": true, "next_action": null }

7 Test your integration

Select any bank in the iDEAL bank list with your test API keys. After confirming the payment, you’re redirected to a test page with options to succeed or fail the payment. You can test the successful payment case by authenticating the payment on the redirect page. The PaymentIntent will transition from requires_action to succeeded.

To test the case where the user fails to authenticate, select any bank with your test API keys. On the redirect page, click Fail test payment. Your PaymentIntent will transition from requires_action to requires_payment_method.

Test your SEPA Direct Debit integration

When you create an iDEAL PaymentMethod with your test API keys, you can configure it to store different test account numbers in the generated SEPA Direct Debit PaymentMethod. These account numbers will not change the outcome of the initial iDEAL PaymentIntent. PaymentIntents confirmed with the generated SEPA Direct Debit PaymentMethod will follow the behavior described below.

When confirming your iDEAL PaymentIntent in test mode, set payment_method.billing_details.email to one of the following values to change the behavior of the generated SEPA Direct Debit PaymentMethod.

You can optionally include your own custom text at the beginning of the email address followed by an underscore. For example, "test_1_generatedSepaDebitIntentsFail@example.com" will result in a SEPA Direct Debit PaymentMethod that will always fail when used in a PaymentIntent.

Email Address Description
generatedSepaDebitIntentsSucceed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
generatedSepaDebitIntentsSucceedDelayed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
generatedSepaDebitIntentsFail@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
generatedSepaDebitIntentsFailDelayed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
generatedSepaDebitIntentsSucceedDisputed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.

Instead of creating a PaymentMethod, you can use an existing test iDEAL PaymentMethod to change the generated SEPA Direct Debit PaymentMethod’s behavior. These PaymentMethod tokens can be useful for automated testing if you want to immediately attach the PaymentMethod to the Sofort PaymentIntent on the server.

Payment Method Description
pm_ideal_generatedSepaDebitIntentsSucceed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
pm_ideal_generatedSepaDebitIntentsSucceedDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
pm_ideal_generatedSepaDebitIntentsFail The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
pm_ideal_generatedSepaDebitIntentsFailDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
pm_ideal_generatedSepaDebitIntentsSucceedDisputed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.

Optional Handle post-payment events

Stripe sends a setup_intent.succeeded event when the SetupIntent completes. Use the Dashboard, a custom webhook, or a partner solution to receive these events and run actions, like saving the generated SEPA Direct Debit PaymentMethod to your database or charging your customer after a trial period.

Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes. Setting up your integration to listen for asynchronous events also makes it easier to accept more payment methods in the future. Check out our guide to payment methods to see the differences between all supported payment methods.

Receive events and run business actions

Manually

Use the Stripe Dashboard to view all your Stripe payments, send email receipts, handle payouts, or retry failed payments.

Custom code

Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.

Prebuilt apps

Handle common business events, like shipping and inventory management, by integrating a partner application.

Setting up future SEPA Direct Debit payments using iDEAL in your app consists of creating a SetupIntent object to track the process, collecting mandate acknowledgement, and redirecting your customer to iDEAL. Stripe uses the SetupIntent to track and handle all the states of the setup until the setup completes.

1 Set up Stripe Server-side Client-side

First, you need a Stripe account. Register now.

Server-side

This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

# 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

Client-side

The Android SDK is open source and fully documented.

To install the SDK, add stripe-android to the dependencies block of your app/build.gradle file:

apply plugin: 'com.android.application' android { ... } dependencies { // ... // Stripe Android SDK implementation 'com.stripe:stripe-android:16.1.0' }

Configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API, such as in your Application subclass:

import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext, "pk_test_TYooMQauvdEDq54NiTphI7jx" ) } }
import com.stripe.android.PaymentConfiguration; public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); PaymentConfiguration.init( getApplicationContext(), "pk_test_TYooMQauvdEDq54NiTphI7jx" ); } }

Our code samples also use OkHttp and GSON to make HTTP requests to a server.

2 Create or retrieve a Customer Server-side

To use a SEPA Direct Debit PaymentMethod 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.

When you include customer in your SetupIntent before confirming, Stripe automatically attaches the generated SEPA Direct Debit Payment Method to the provided Customer object.

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 SetupIntent Server-side

A SetupIntent is an object that represents your intent to set up a customer’s payment method for future payments. The SetupIntent will track the steps of this set-up process. For iDEAL, this includes collecting a SEPA Direct Debit mandate from the customer and tracking its validity throughout its lifecycle.

Create a SetupIntent on your server with payment_method_types set to ideal and specify the Customer’s id.

curl https://api.stripe.com/v1/setup_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=ideal \ -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' setup_intent = Stripe::SetupIntent.create({ payment_method_types: ['ideal'], customer: customer['id'], }) end
# 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' setup_intent = stripe.SetupIntent.create( payment_method_types=['ideal'], 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'); $setup_intent = \\Stripe\\SetupIntent::create([ 'payment_method_types' => ['ideal'], '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"; Map<String, Object> params = new HashMap<>(); List<Object> paymentMethodTypes = new ArrayList<>(); paymentMethodTypes.add("ideal"); params.put("payment_method_types", paymentMethodTypes); params.put("customer", customer.getId()); SetupIntent setupIntent = SetupIntent.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 setupIntent = await stripe.setupIntents.create({ payment_method_types: ['ideal'], 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.SetupIntentParams{ PaymentMethodTypes: stripe.StringSlice([]string{"ideal"}), Customer: stripe.String(c.ID), } si, _ := setupintent.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 SetupIntentCreateOptions { PaymentMethodTypes = new List<string> { "ideal" }, Customer = customer.Id, }; var service = new SetupIntentService(); service.Create(options);

4 Collect payment method details and mandate acknowledgement Client-side

In your app, collect your customer’s full name, email address, and the name of their bank (e.g., abn_amro). Create a PaymentMethodCreateParams object with these details.

val billingDetails = PaymentMethod.BillingDetails(name = "Jenny Rosen", email = "jenny.rosen@example.com") val ideal = PaymentMethodCreateParams.Ideal("abn_amro") val paymentMethodCreateParams = PaymentMethodCreateParams.create(ideal, billingDetails)
PaymentMethod.BillingDetails billingDetails = new PaymentMethod.BillingDetails.Builder() .setName("Jenny Rosen") .setEmail("jenny.rosen@example.com") .build(); PaymentMethodCreateParams.Ideal ideal = new PaymentMethodCreateParams.Ideal("abn_amro") PaymentMethodCreateParams paymentMethodCreateParams = PaymentMethodCreateParams.create(ideal, billingDetails);

To process SEPA Direct Debit payments in the future, you must collect mandate agreement from your customer now. 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 payment method. Because the customer has implicitly signed the mandate when accepting the terms suggested above, you must communicate the terms on the form or in an email.

5 Submit the payment method details to Stripe Client-side

Retrieve the client secret from the SetupIntent you created in step 2 and call the stripe confirmSetupIntent method. This presents a webview where the customer can complete the setup on their bank’s website or app. Afterwards, onActivityResult is called with the result of the payment.

class IdealSetupActivity : AppCompatActivity() { // ... private lateinit var setupIntentClientSecret: String private val stripe: Stripe by lazy { Stripe( applicationContext, PaymentConfiguration.getInstance(applicationContext).publishableKey ) } private fun startCheckout() { // ... val confirmParams = ConfirmSetupIntentParams .create( paymentMethodCreateParams = paymentMethodCreateParams, clientSecret = setupIntentClientSecret ) stripe.confirmSetupIntent(confirmParams) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // Handle the result of stripe.confirmSetupIntent stripe.onSetupResult(requestCode, data, object : ApiResultCallback<SetupIntentResult> { override fun onSuccess(result: SetupIntentResult) { val setupIntent = result.intent val status = setupIntent.status when (status) { StripeIntent.Status.Succeeded -> { // Setup succeeded } else -> { // Setup failed/cancelled } } } override fun onError(e: Exception) { // Setup failed } }) } }
public class IdealSetupActivity extends AppCompatActivity { // ... private String setupIntentClientSecret; private Stripe stripe; private void startCheckout() { // ... ConfirmSetupIntentParams = ConfirmSetupIntentParams .create( paymentMethodCreateParams, setupIntentClientSecret ); final Context context = getApplicationContext(); stripe = new Stripe( context, PaymentConfiguration.getInstance(context).getPublishableKey() ); stripe.confirmSetupIntent(this, confirmParams); } // ... @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Handle the result of stripe.confirmSetupIntent stripe.ononSetupResultrequestCode, data, new SetupResultCallback()); } // ... private static final class SetupResultCallback implements ApiResultCallback<SetupIntentResult> { @Override public void onSuccess(@NonNull SetupIntentResult result) { SetupIntent setupIntent = result.getIntent(); StripeIntent.Status status = setupIntent.getStatus(); if (status == StripeIntent.Status.Succeeded) { // Setup succeeded } else { // Setup failed/cancelled } } @Override public void onError(@NonNull Exception e) { // Setup failed } } }

6 Charge the SEPA Direct Debit PaymentMethod later

When you are ready to charge your customer, use the Customer and SEPA Direct Debit PaymentMethod IDs to create a new PaymentIntent.

You can find the ID of the generated SEPA Direct Debit PaymentMethod by listing the PaymentMethods associated with your Customer.

curl https://api.stripe.com/v1/payment_methods \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d customer="{{CUSTOMER_ID}}" \ -d type=sepa_debit
Stripe::PaymentMethod.list({ customer: '{{CUSTOMER_ID}}', type: 'sepa_debit', })
stripe.PaymentMethod.list( customer="{{CUSTOMER_ID}}", type="sepa_debit", )
$stripe->paymentMethods->all([ 'customer' => '{{CUSTOMER_ID}}', 'type' => 'sepa_debit', ]);
Map<String, Object> params = new HashMap<>(); params.put("customer", "{{CUSTOMER_ID}}"); params.put("type", "sepa_debit"); PaymentMethodCollection paymentMethods = PaymentMethod.list(params);
stripe.paymentMethods.list( {customer: '{{CUSTOMER_ID}}', type: 'sepa_debit'}, function(err, paymentMethods) { // asynchronously called } );
params := &stripe.PaymentMethodListParams{ Customer: stripe.String("{{CUSTOMER_ID}}"), Type: stripe.String("sepa_debit"), } i := paymentmethod.List(params) for i.Next() { pm := i.PaymentMethod() }
var options = new PaymentMethodListOptions { Customer = "{{CUSTOMER_ID}}", Type = "sepa_debit", }; var service = new PaymentMethodService(); StripeList<PaymentMethod> paymentMethods = service.List( options );

Next, create a PaymentIntent with payment_method set to the ID of the SEPA Direct Debit PaymentMethod and customer set to the ID of the Customer.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=sepa_debit \ -d amount=1099 \ -d currency=eur \ -d customer="{{CUSTOMER_ID}}" \ -d payment_method="{{SEPA_DEBIT_PAYMENT_METHOD_ID}}" \ -d confirm=true
# 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({ payment_method_types: ['sepa_debit'], amount: 1099, currency: 'eur', customer: '{{CUSTOMER_ID}}', payment_method: '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm: true, })
# 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( payment_method_types=['sepa_debit'], amount=1099, currency='eur', customer='{{CUSTOMER_ID}}', payment_method='{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm=True, )
// 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([ 'payment_method_types' => ['sepa_debit'], 'amount' => 1099, 'currency' => 'eur', 'customer' => '{{CUSTOMER_ID}}', 'payment_method' => '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', 'confirm' => true, ]);
// 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") .setCustomer("{{CUSTOMER_ID}}") .setPaymentMethod("{{SEPA_DEBIT_PAYMENT_METHOD_ID}}") .addPaymentMethodType("sepa_debit") .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 paymentIntent = await stripe.paymentIntents.create({ payment_method_types: ['sepa_debit'], amount: 1099, currency: 'eur', customer: '{{CUSTOMER_ID}}', payment_method: '{{SEPA_DEBIT_PAYMENT_METHOD_ID}}', confirm: true, });
// 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))), Customer: stripe.String("{{CUSTOMER_ID}}"), PaymentMethod: stripe.String("{{SEPA_DEBIT_PAYMENT_METHOD_ID}}"), PaymentMethodTypes: stripe.StringSlice([]string{ "sepa_debit", }), } 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", Customer = "{{CUSTOMER_ID}}", PaymentMethod = "{{SEPA_DEBIT_PAYMENT_METHOD_ID}}", PaymentMethodTypes = new List<string> { "sepa_debit", }, }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options);

For more information about testing your SEPA Direct Debit integration, see our full guide on accepting payments with SEPA Direct Debit.

Additional information about the iDEAL payment can be found on the iDEAL PaymentIntent’s successful Charge, under the payment_method_details property.

{ "charges": { "data": [ { "payment_method_details": { "ideal": { "bank": "ing", "bic": "INGBNL2A", "iban_last4": "****", "generated_sepa_debit": "pm_1GrddXGf98efjktuBIi3ag7aJQ", "verified_name": "JENNY ROSEN" }, "type": "ideal" }, "id": "src_16xhynE8WzK49JbAs9M21jaR",
See all 49 lines "object": "source", "amount": 1099, "client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU", "created": 1445277809, "currency": "eur", "flow": "redirect", "livemode": true, "statement_descriptor": null, "status": "pending", "type": "ideal", "usage": "single_use" } ], "object": "list", "has_more": false, "url": "/v1/charges?payment_intent=pi_1G1sgdKi6xqXeNtkldRRE6HT" }, "payment_method_options": { "ideal": {} }, "payment_method_types": [ "ideal" ], "id": "pi_1G1sgdKi6xqXeNtkldRRE6HT", "object": "payment_intent", "amount": 1099, "client_secret": "pi_1G1sgdKi6xqXeNtkldRRE6HT_secret_h9B56ObhTN72fQiBAuzcVPb2E", "confirmation_method": "automatic", "created": 1579259303, "currency": "eur", "customer": "cus_f0Us034jfkXcl0CJQ", "livemode": true, "next_action": null }

7 Test your integration

Select any bank in the iDEAL bank list with your test API keys. After confirming the payment, you’re redirected to a test page with options to succeed or fail the payment. You can test the successful payment case by authenticating the payment on the redirect page. The PaymentIntent will transition from requires_action to succeeded.

To test the case where the user fails to authenticate, select any bank with your test API keys. On the redirect page, click Fail test payment. Your PaymentIntent will transition from requires_action to requires_payment_method.

Test your SEPA Direct Debit integration

When you create an iDEAL PaymentMethod with your test API keys, you can configure it to store different test account numbers in the generated SEPA Direct Debit PaymentMethod. These account numbers will not change the outcome of the initial iDEAL PaymentIntent. PaymentIntents confirmed with the generated SEPA Direct Debit PaymentMethod will follow the behavior described below.

When confirming your iDEAL PaymentIntent in test mode, set payment_method.billing_details.email to one of the following values to change the behavior of the generated SEPA Direct Debit PaymentMethod.

You can optionally include your own custom text at the beginning of the email address followed by an underscore. For example, "test_1_generatedSepaDebitIntentsFail@example.com" will result in a SEPA Direct Debit PaymentMethod that will always fail when used in a PaymentIntent.

Email Address Description
generatedSepaDebitIntentsSucceed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
generatedSepaDebitIntentsSucceedDelayed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
generatedSepaDebitIntentsFail@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
generatedSepaDebitIntentsFailDelayed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
generatedSepaDebitIntentsSucceedDisputed@example.com The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.

Instead of creating a PaymentMethod, you can use an existing test iDEAL PaymentMethod to change the generated SEPA Direct Debit PaymentMethod’s behavior. These PaymentMethod tokens can be useful for automated testing if you want to immediately attach the PaymentMethod to the Sofort PaymentIntent on the server.

Payment Method Description
pm_ideal_generatedSepaDebitIntentsSucceed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
pm_ideal_generatedSepaDebitIntentsSucceedDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
pm_ideal_generatedSepaDebitIntentsFail The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
pm_ideal_generatedSepaDebitIntentsFailDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
pm_ideal_generatedSepaDebitIntentsSucceedDisputed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded, but a dispute is immediately created.

Optional Handle post-payment events

Stripe sends a setup_intent.succeeded event when the SetupIntent completes. Use the Dashboard, a custom webhook, or a partner solution to receive these events and run actions, like saving the generated SEPA Direct Debit PaymentMethod to your database or charging your customer after a trial period.

Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes. Setting up your integration to listen for asynchronous events also makes it easier to accept more payment methods in the future. Check out our guide to payment methods to see the differences between all supported payment methods.

Receive events and run business actions

Manually

Use the Stripe Dashboard to view all your Stripe payments, send email receipts, handle payouts, or retry failed payments.

Custom code

Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.

Prebuilt apps

Handle common business events, like shipping and inventory management, by integrating a partner application.

See also

Congrats, you are done with your integration! You can learn more about accepting SEPA Direct Debit payments and setting up Subscriptions with SEPA Direct Debit.