Payments
Bank redirects
iDEAL
Save bank details during payment

Save bank details during an iDEAL payment

Learn how to save your customer’s IBAN bank details from an iDEAL payment.

iDEAL is a popular single use payment method in the Netherlands where customers are required to authenticate their payment. Customers pay with iDEAL by redirecting to a webview, authorizing the payment, then returning to your app where you get immediate notification on whether the payment succeeded or failed.

You can use the Payment Intents API to accept an initial iDEAL payment and save your customer’s IBAN bank details into a SEPA Direct Debit PaymentMethod. You can use the SEPA Direct Debit PaymentMethod to accept payments or set up a Subscription.

When you use iDEAL to save bank details, you reduce friction for your customer because they do not have to type their IBAN. In addition, you will receive their verified name and validated IBAN.

Accepting iDEAL payments consists of creating a PaymentIntent object to track a payment, collecting payment method information and mandate acknowledgement, and submitting the payment to Stripe for processing. Stripe uses the PaymentIntent to track and handle all the states of the payment until the payment completes. Use the ID of the SEPA Direct Debit PaymentMethod collected from your initial iDEAL PaymentIntent to create PaymentIntents for future payments.

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

A PaymentIntent is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage. First, create a PaymentIntent on your server and specify the amount to collect and the eur currency (iDEAL does not support other currencies). If you already have an integration using the Payment Intents API, add ideal to the list of payment method types for your PaymentIntent.

Include the setup_future_usage and customer parameters. When you provide these parameters, Stripe creates a SEPA Direct Debit Payment Method object, attaches it to your specified Customer and returns the SEPA Direct Debit Payment Method ID in the successful Payment Intent body.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=ideal \ -d customer="{{CUSTOMER_ID}}" \ -d setup_future_usage=off_session
# 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' payment_intent = Stripe::PaymentIntent.create({ amount: 1099, currency: 'eur', payment_method_types: ['ideal'], customer: customer['id'], setup_future_usage: 'off_session', })
# 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' payment_intent = stripe.PaymentIntent.create( amount=1099, currency='eur', payment_method_types=['ideal'], customer=customer['id'], setup_future_usage='off_session' )
// 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'); $payment_intent = \Stripe\PaymentIntent::create([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['ideal'], 'customer' => $customer->id, 'setup_future_usage' => 'off_session', ]);
// 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") .addPaymentMethodType("ideal") .setSetupFutureUsage(SessionCreateParams.PaymentIntentData.SetupFutureUsage.OFF_SESSION) .build(); PaymentIntent paymentIntent = PaymentIntent.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['ideal'], customer: customer.id, setup_future_usage: 'off_session', });
// 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)), PaymentMethodTypes: stripe.StringSlice([]string{ "ideal", }), SetupFutureUsage: stripe.String(string(stripe.PaymentIntentSetupFutureUsageOffSession)), } 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", PaymentMethodTypes = new List<string> { "ideal", }, SetupFutureUsage = "off_session", }; var service = new PaymentIntentService(); var intent = service.Create(options);

Included in the returned PaymentIntent is a client secret, which is used on the client side to securely complete the payment process instead of passing the entire PaymentIntent object. There are different approaches that you can use to pass the client secret to the client side.

You can retrieve the client secret from an endpoint on your server using the browser’s fetch function on the client side. This approach is generally most suitable when your client side is a single-page application, particularly one built with a modern frontend framework such as React. This example shows how to create the server endpoint that serves the client secret:

get '/secret' do intent = # ... Create or retrieve the PaymentIntent {client_secret: intent.client_secret}.to_json end
from flask import Flask, jsonify app = Flask(__name__) @app.route('/secret') def secret(): intent = # ... Create or retrieve the PaymentIntent return jsonify(client_secret=intent.client_secret)
<?php $intent = # ... Create or retrieve the PaymentIntent echo json_encode(array('client_secret' => $intent->client_secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.PaymentIntent; import com.google.gson.Gson; import static spark.Spark.get; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); get("/secret", (request, response) -> { PaymentIntent intent = // ... Fetch or create the PaymentIntent Map<String, String> map = new HashMap(); map.put("client_secret", intent.getClientSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.get('/secret', async (req, res) => { const intent = // ... Fetch or create the PaymentIntent res.json({client_secret: intent.client_secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" stripe "github.com/stripe/stripe-go/v71" ) type CheckoutData struct { ClientSecret string `json:"client_secret"` } func main() { http.HandleFunc("/secret", func(w http.ResponseWriter, r *http.Request) { intent := // ... Fetch or create the PaymentIntent data := CheckoutData{ ClientSecret: intent.ClientSecret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; using Stripe; namespace StripeExampleApi.Controllers { [Route("secret")] [ApiController] public class CheckoutApiController : Controller { [HttpGet] public ActionResult Get() { var intent = // ... Fetch or create the PaymentIntent return Json(new {client_secret = intent.ClientSecret}); } } }

This example demonstrates how to fetch the client secret with JavaScript on the client side:

var response = fetch('/secret').then(function(response) { return response.json(); }).then(function(responseJson) { var clientSecret = responseJson.client_secret; // Call stripe.confirmIdealPayment() with the client secret. });
(async () => { const response = await fetch('/secret'); const {client_secret: clientSecret} = await response.json(); // Call stripe.confirmIdealPayment() with the client secret. })();

If your application uses server-side rendering, you may wish to use your template framework to embed the client secret in the HTML output of your checkout page during rendering. You can embed it in a data attribute or hidden HTML element and then extract it with JavaScript in order to use it to complete payment.

<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="<%= @intent.client_secret %>">Submit Payment</button>
get '/checkout' do @intent = # ... Fetch or create the PaymentIntent erb :checkout end
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
@app.route('/checkout') def checkout(): intent = # ... Fetch or create the PaymentIntent return render_template('checkout.html', client_secret=intent.client_secret)
<?php $intent = # ... Fetch or create the PaymentIntent; ?> ... <input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="<?= $intent->client_secret ?>"> Submit Payment </button> ...
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
import java.util.HashMap; import java.util.Map; import com.stripe.model.PaymentIntent; import spark.ModelAndView; import static spark.Spark.get; public class StripeJavaQuickStart { public static void main(String[] args) { get("/checkout", (request, response) -> { PaymentIntent intent = // ... Fetch or create the PaymentIntent Map map = new HashMap(); map.put("client_secret", intent.getClientSecret()); return new ModelAndView(map, "checkout.hbs"); }, new HandlebarsTemplateEngine()); } }
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
const express = require('express'); const expressHandlebars = require('express-handlebars'); const app = express(); app.engine('.hbs', expressHandlebars({ extname: '.hbs' })); app.set('view engine', '.hbs'); app.set('views', './views'); app.get('/checkout', async (req, res) => { const intent = // ... Fetch or create the PaymentIntent res.render('checkout', { client_secret: intent.client_secret }); }); app.listen(3000, () => { console.log('Running on port 3000'); });
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="{{ .ClientSecret }}"> Submit Payment </button>
package main import ( "html/template" "net/http" stripe "github.com/stripe/stripe-go/v71" ) type CheckoutData struct { ClientSecret string } func main() { checkoutTmpl := template.Must(template.ParseFiles("views/checkout.html")) http.HandleFunc("/checkout", func(w http.ResponseWriter, r *http.Request) { intent := // ... Fetch or create the PaymentIntent data := CheckoutData{ ClientSecret: intent.ClientSecret, } checkoutTmpl.Execute(w, data) }) http.ListenAndServe(":3000", nil) }
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="@ViewData["ClientSecret"]"> Submit Payment </button>
using System; using Microsoft.AspNetCore.Mvc; using Stripe; namespace StripeExampleApi.Controllers { [Route("/[controller]")] public class CheckoutController : Controller { public IActionResult Index() { var intent = // ... Fetch or create the PaymentIntent ViewData["ClientSecret"] = intent.ClientSecret; return View(); } } }

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 a HTTPS connection. The checkout page address must also start with https:// rather than http:// for your integration to work.

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

Set up Stripe Elements

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

<head> <title>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 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.

<form id="payment-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="accountholder-email"> Email </label> <input id="accountholder-email" name="accountholder-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> <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 to Stripe Client-side

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

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

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

Include your customer’s name and email address in payment_method[billing_details]. They will be used when generating the SEPA Direct Debit PaymentMethod.

var form = document.getElementById('payment-form'); var accountholderName = document.getElementById('accountholder-name'); var accountholderEmail = document.getElementById('accountholder-email'); var submitButton = document.getElementById('submit-button'); var clientSecret = submitButton.dataset.secret; form.addEventListener('submit', function(event) { event.preventDefault(); // Redirects away from the client stripe.confirmIdealPayment( clientSecret, { payment_method: { ideal: idealBank, billing_details: { name: accountholderName.value, email: accountholderEmail.value, }, }, return_url: 'https://your-website.com/checkout/complete', } ); });
const form = document.getElementById('payment-form'); const accountholderName = document.getElementById('accountholder-name'); form.addEventListener('submit', (event) => { event.preventDefault(); // Redirects away from the client const {error} = await stripe.confirmIdealPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { ideal: idealBank, billing_details: { name: accountholderName.value, }, }, return_url: 'https://your-website.com/checkout/complete', } ); });

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.confirmIdealPayment to handle the redirect away from your page and to complete the payment. Add a return_url to this function to indicate where Stripe should redirect the user after they complete the payment on their bank’s website or mobile application.

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

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

import React from 'react'; import {useStripe, useElements, 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.confirmIdealPayment('{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 payment 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 Payment </button> {/* Display mandate acceptance text. */} <div id="mandate-acceptance"> By providing your payment information and confirming this payment, you authorise (A) Rocketship Inc and Stripe, our payment service provider, to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with those instructions. As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited. Your rights are explained in a statement that you can obtain from your bank. You agree to receive notifications for future debits up to 2 days before they occur. </div> </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.confirmIdealPayment('{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 payment 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 Payment </button> {/* Display mandate acceptance text. */} <div id="mandate-acceptance"> By providing your payment information and confirming this payment, you authorise (A) Rocketship Inc and Stripe, our payment service provider, to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with those instructions. As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited. Your rights are explained in a statement that you can obtain from your bank. You agree to receive notifications for future debits up to 2 days before they occur. </div> </form> ); } } export default function InjectedCheckoutForm() { return ( <ElementsConsumer> {({stripe, elements}) => ( <CheckoutForm stripe={stripe} elements={elements} /> )} </ElementsConsumer> ); }

The return_url should correspond to a page on your website that provides the status of the payment, by verifying the status of the PaymentIntent when rendering the return page. 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
payment_intent The unique identifier for the PaymentIntent
payment_intent_client_secret The client secret of the PaymentIntent object

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 payment_intent.succeeded event when the payment completes. Use the Dashboard, a custom webhook, or a partner solution to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.

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.

Optional Handle the iDEAL redirect manually

We recommend relying on Stripe.js to handle iDEAL redirects and payments with confirmIdealPayment. However, you can also manually redirect your customers by:

  1. Providing the URL where your customers will be redirected after they complete their payment.
curl https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/confirm \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d return_url="https://your-website.com/checkout/complete"
# 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.confirm( '{{PAYMENT_INTENT_ID}}', { return_url: 'https://your-website.com/checkout/complete', } )
# 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.confirm( '{{PAYMENT_INTENT_ID}}', return_url='https://your-website.com/checkout/complete', )
// 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'); $payment_intent = \Stripe\PaymentIntent::retrieve('{{PAYMENT_INTENT_ID}}'); $payment_intent->confirm([ 'return_url' => 'https://your-website.com/checkout/complete', ]);
// 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"; PaymentIntent paymentIntent = PaymentIntent.retrieve("{{PAYMENT_INTENT_ID}}"); PaymentIntentConfirmParams params = PaymentIntentConfirmParams.builder() .setReturnUrl("https://your-website.com/checkout/complete") .build(); paymentIntent = paymentIntent.confirm(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.confirm( '{{PAYMENT_INTENT_ID}}', { return_url: 'https://your-website.com/checkout/complete', } );
// 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.PaymentIntentConfirmParams{ ReturnURL: stripe.String("https://your-website.com/checkout/complete"), } pi, _ := paymentintent.Confirm("{{PAYMENT_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 options = new PaymentIntentConfirmOptions { ReturnUrl = "https://your-website.com/checkout/complete", }; var service = new PaymentIntentService(); var intent = service.Confirm("{{PAYMENT_INTENT_ID}}", options);
  1. Confirming the PaymentIntent has a status of requires_action. The type for the next_action will be redirect_to_url.
{ "next_action": { "type": "redirect_to_url", "redirect_to_url": { "url": "https://hooks.stripe.com/...", "return_url": "https://your-website.com/checkout/complete" } }, "charges": { "data": [ { "payment_method_details": { "ideal": { "bank": "ing", "bic": "INGBNL2A",
See all 53 lines "iban_last4": "****", "verified_name": "JENNY ROSEN" }, "type": "ideal" }, "id": "src_16xhynE8WzK49JbAs9M21jaR", "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", "livemode": true }
  1. Redirecting the customer to the URL provided in the next_action property.
var action = intent.next_action; if (action && action.type === 'redirect_to_url') { window.location = action.redirect_to_url.url; }
const action = intent.next_action; if (action && action.type === 'redirect_to_url') { window.location = action.redirect_to_url.url; }

When the customer finishes the payment process, they are sent to the return_url destination. The payment_intent and payment_intent_client_secret URL query parameters are included and you may pass through your own query parameters, as described above.

Accepting iDEAL payments consists of creating a PaymentIntent object to track a payment, collecting payment method information and mandate acknowledgement, and submitting the payment to Stripe for processing. Stripe uses the PaymentIntent to track and handle all the states of the payment until the payment completes. Use the ID of the SEPA Direct Debit PaymentMethod collected from your initial iDEAL PaymentIntent to create PaymentIntents for future payments.

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 { Stripe.setDefaultPublishableKey("pk_test_TYooMQauvdEDq54NiTphI7jx") // do any other necessary launch configuration return true } }
#import "AppDelegate.h" #import <Stripe/Stripe.h> @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Stripe 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 PaymentIntent 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 PaymentIntent Server-side

A PaymentIntent is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage. First, create a PaymentIntent on your server and specify the amount to collect and the eur currency (iDEAL does not support other currencies). If you already have an integration using the Payment Intents API, add ideal to the list of payment method types for your PaymentIntent.

Include the setup_future_usage and customer parameters. When you provide these parameters, Stripe creates a SEPA Direct Debit Payment Method object, attaches it to your specified Customer and returns the SEPA Direct Debit Payment Method ID in the successful Payment Intent body.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=ideal \ -d customer="{{CUSTOMER_ID}}" \ -d setup_future_usage=off_session
# 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' payment_intent = Stripe::PaymentIntent.create({ amount: 1099, currency: 'eur', payment_method_types: ['ideal'], customer: customer['id'], setup_future_usage: 'off_session', })
# 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' payment_intent = stripe.PaymentIntent.create( amount=1099, currency='eur', payment_method_types=['ideal'], customer=customer['id'], setup_future_usage='off_session' )
// 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'); $payment_intent = \Stripe\PaymentIntent::create([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['ideal'], 'customer' => $customer->id, 'setup_future_usage' => 'off_session', ]);
// 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") .addPaymentMethodType("ideal") .setSetupFutureUsage(SessionCreateParams.PaymentIntentData.SetupFutureUsage.OFF_SESSION) .build(); PaymentIntent paymentIntent = PaymentIntent.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['ideal'], customer: customer.id, setup_future_usage: 'off_session', });
// 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)), PaymentMethodTypes: stripe.StringSlice([]string{ "ideal", }), SetupFutureUsage: stripe.String(string(stripe.PaymentIntentSetupFutureUsageOffSession)), } 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", PaymentMethodTypes = new List<string> { "ideal", }, SetupFutureUsage = "off_session", }; var service = new PaymentIntentService(); var intent = service.Create(options);

Included in the returned PaymentIntent is a client secret, which is used on the client side to securely complete the payment process instead of passing the entire PaymentIntent object.

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

5 Submit the payment to Stripe Client-side

Retrieve the client secret from the PaymentIntent you created in step 3 and call STPPaymentHandler confirmPayment. 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 paymentIntentParams = STPPaymentIntentParams(clientSecret: paymentIntentClientSecret) paymentIntentParams.paymentMethodParams = paymentMethodParams STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: self) { (handlerStatus, paymentIntent, error) in switch handlerStatus { case .succeeded: // Payment succeeded case .canceled: // Payment was cancelled case .failed: // Payment failed @unknown default: fatalError() } }
STPPaymentIntentParams *paymentIntentParams = [[STPPaymentIntentParams alloc] initWithClientSecret:paymentIntentClientSecret]; paymentIntentParams.paymentMethodParams = paymentMethodParams; [[STPPaymentHandler sharedHandler] confirmPayment:paymentIntentParams withAuthenticationContext:self completion:^(STPPaymentHandlerActionStatus handlerStatus, STPPaymentIntent * handledIntent, NSError * _Nullable handlerError) { switch (handlerStatus) { case STPPaymentHandlerActionStatusFailed: // Payment failed break; case STPPaymentHandlerActionStatusCanceled: // Payment was cancelled break; case STPPaymentHandlerActionStatusSucceeded: // Payment 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 payment_intent.succeeded event when the payment completes. Use the Dashboard, a custom webhook, or a partner solution to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.

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.

Accepting iDEAL payments consists of creating a PaymentIntent object to track a payment, collecting payment method information and mandate acknowledgement, and submitting the payment to Stripe for processing. Stripe uses the PaymentIntent to track and handle all the states of the payment until the payment completes. Use the ID of the SEPA Direct Debit PaymentMethod collected from your initial iDEAL PaymentIntent to create PaymentIntents for future payments.

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.0.1' }

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

A PaymentIntent is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage. First, create a PaymentIntent on your server and specify the amount to collect and the eur currency (iDEAL does not support other currencies). If you already have an integration using the Payment Intents API, add ideal to the list of payment method types for your PaymentIntent.

Include the setup_future_usage and customer parameters. When you provide these parameters, Stripe creates a SEPA Direct Debit Payment Method object, attaches it to your specified Customer and returns the SEPA Direct Debit Payment Method ID in the successful Payment Intent body.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=ideal \ -d customer="{{CUSTOMER_ID}}" \ -d setup_future_usage=off_session
# 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' payment_intent = Stripe::PaymentIntent.create({ amount: 1099, currency: 'eur', payment_method_types: ['ideal'], customer: customer['id'], setup_future_usage: 'off_session', })
# 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' payment_intent = stripe.PaymentIntent.create( amount=1099, currency='eur', payment_method_types=['ideal'], customer=customer['id'], setup_future_usage='off_session' )
// 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'); $payment_intent = \Stripe\PaymentIntent::create([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['ideal'], 'customer' => $customer->id, 'setup_future_usage' => 'off_session', ]);
// 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") .addPaymentMethodType("ideal") .setSetupFutureUsage(SessionCreateParams.PaymentIntentData.SetupFutureUsage.OFF_SESSION) .build(); PaymentIntent paymentIntent = PaymentIntent.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['ideal'], customer: customer.id, setup_future_usage: 'off_session', });
// 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)), PaymentMethodTypes: stripe.StringSlice([]string{ "ideal", }), SetupFutureUsage: stripe.String(string(stripe.PaymentIntentSetupFutureUsageOffSession)), } 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", PaymentMethodTypes = new List<string> { "ideal", }, SetupFutureUsage = "off_session", }; var service = new PaymentIntentService(); var intent = service.Create(options);

Included in the returned PaymentIntent is a client secret, which is used on the client side to securely complete the payment process instead of passing the entire PaymentIntent object.

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

5 Submit the payment to Stripe Client-side

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

class IdealPaymentActivity : AppCompatActivity() { // ... private lateinit var paymentIntentClientSecret: String private val stripe: Stripe by lazy { Stripe( applicationContext, PaymentConfiguration.getInstance(applicationContext).publishableKey ) } private fun startCheckout() { // ... val confirmParams = ConfirmPaymentIntentParams .createWithPaymentMethodCreateParams( paymentMethodCreateParams = paymentMethodCreateParams, clientSecret = paymentIntentClientSecret ) stripe.confirmPayment(confirmParams) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // Handle the result of stripe.confirmPayment stripe.onPaymentResult(requestCode, data, object : ApiResultCallback<PaymentIntentResult> { override fun onSuccess(result: PaymentIntentResult) { val paymentIntent = result.intent val status = paymentIntent.status when (status) { StripeIntent.Status.Succeeded -> { // Payment succeeded } else -> { // Payment failed/cancelled } } } override fun onError(e: Exception) { // Payment failed } }) } }
public class IdealPaymentActivity extends AppCompatActivity { // ... private String paymentIntentClientSecret; private Stripe stripe; private void startCheckout() { // ... ConfirmPaymentIntentParams = ConfirmPaymentIntentParams .createWithPaymentMethodCreateParams( paymentMethodCreateParams, paymentIntentClientSecret ); final Context context = getApplicationContext(); stripe = new Stripe( context, PaymentConfiguration.getInstance(context).getPublishableKey() ); stripe.confirmPayment(this, confirmParams); } // ... @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Handle the result of stripe.confirmPayment stripe.onPaymentResult(requestCode, data, new PaymentResultCallback()); } // ... private static final class PaymentResultCallback implements ApiResultCallback<PaymentIntentResult> { @Override public void onSuccess(@NonNull PaymentIntentResult result) { PaymentIntent paymentIntent = result.getIntent(); StripeIntent.Status status = paymentIntent.getStatus(); if (status == StripeIntent.Status.Succeeded) { // Payment succeeded } else { // Payment failed/cancelled } } @Override public void onError(@NonNull Exception e) { // Payment 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 payment_intent.succeeded event when the payment completes. Use the Dashboard, a custom webhook, or a partner solution to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.

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.

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