Payments
Bank redirects
Bancontact
Save bank details during payment

Save bank details during a Bancontact payment

Learn how to save your customer’s IBAN bank details from a Bancontact payment.

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

You can use the Payment Intents API to accept an initial Bancontact 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 Bancontact 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 Bancontact 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 Bancontact 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 (Bancontact doesn’t support other currencies). If you already have an integration using the Payment Intents API, add bancontact 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[]"=bancontact \ -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: ['bancontact'], 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=['bancontact'], 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' => ['bancontact'], '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("bancontact") .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: ['bancontact'], 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{ "bancontact", }), 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> { "bancontact", }, 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.confirmBancontactPayment() with the client secret. });
(async () => { const response = await fetch('/secret'); const {client_secret: clientSecret} = await response.json(); // Call stripe.confirmBancontactPayment() 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

Create a payment form on your client to collect the required billing details from the customer.

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.

Field Value
name The full name (first and last) of the customer.
email The customer’s email.
<form id="payment-form"> <div class="form-row"> <label for="name"> Name </label> <input id="name" name="name" required> </div> <div class="form-row"> <label for="email"> Email </label> <input id="email" name="email" required> </div> <button id="submit-button">Pay with Bancontact</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>

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

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.confirmBancontactPayment 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 stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx'); var accountholderName = document.getElementById('name'); var accountholderEmail = document.getElementById('email'); // Redirects away from the client stripe.confirmBancontactPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { billing_details: { name: accountholderName.value, email: accountholderEmail.value, }, }, return_url: 'https://your-website.com/checkout/complete', } ).then(function(result) { if (result.error) { // Inform the customer that there was an error. } });
var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx'); var accountholderName = document.getElementById('name'); var accountholderEmail = document.getElementById('email'); // Redirects away from the client const {error} = await stripe.confirmBancontactPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { billing_details: { name: accountholderName.value, email: accountholderEmail.value, }, }, return_url: 'https://your-website.com/checkout/complete', } ); if (error) { // Inform the customer that there was an error. }

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.confirmBancontactPayment 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.confirmBancontactPayment 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} from '@stripe/react-stripe-js'; 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; } // 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.confirmBancontactPayment('{CLIENT_SECRET}', { payment_method: { 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> <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} from '@stripe/react-stripe-js'; 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; } // 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.confirmBancontactPayment('{CLIENT_SECRET}', { payment_method: { 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> <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 Bancontact payment can be found on the Bancontact PaymentIntent’s successful Charge, under the payment_method_details property.

{ "charges": { "data": [ { "payment_method_details": { "bancontact": { "bank_code": "VAPE", "bank_name": "VAN DE PUT & CO", "bics": "VAPEBE22", "iban_last4": "7061", "generated_sepa_debit": "pm_1GrddXGf98efjktuBIi3ag7aJQ", "preferred_language": "en", "verified_name": "Jenny Rosen" }, "type": "bancontact"
See all 51 lines }, "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": "bancontact", "usage": "single_use" } ], "object": "list", "has_more": false, "url": "/v1/charges?payment_intent=pi_1G1sgdKi6xqXeNtkldRRE6HT" }, "payment_method_options": { "bancontact": {} }, "payment_method_types": [ "bancontact" ], "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

When you create a Bancontact 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 Bancontact PaymentIntent. PaymentIntents confirmed with the generated SEPA Direct Debit PaymentMethod will follow the behavior described below.

When confirming your Bancontact 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 Bancontact 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_bancontact_generatedSepaDebitIntentsSucceed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
pm_bancontact_generatedSepaDebitIntentsSucceedDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
pm_bancontact_generatedSepaDebitIntentsFail The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
pm_bancontact_generatedSepaDebitIntentsFailDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
pm_bancontact_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 the Bancontact redirect manually

We recommend relying on Stripe.js to handle Bancontact redirects and payments client-side with confirmBancontactPayment. Using Stripe.js makes it much easier to extend your integration to other payment methods. However, you can also manually redirect your customers on your server by following these steps:

  1. Create and confirm a PaymentIntent of type bancontact. You must provide the payment_method_data.billing_details.name property, which you should collect from your customer. Note that, by specifying payment_method_data, a PaymentMethod is created and immediately used with this PaymentIntent.

    You must also provide the URL where your customer is redirected to after they complete their payment in the return_url field. You may optionally provide your own query parameters in this URL. These parameters will be included in the final URL upon completing the redirect flow.
# 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({ confirm: true, amount: 1099, currency: 'eur', payment_method_types: ['bancontact'], payment_method_data: { type: 'bancontact', billing_details: { name: 'Jenny Rosen', }, }, 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.create( confirm=True, amount=1099, currency='eur', payment_method_types=['bancontact'], payment_method_data={ 'type': 'bancontact', 'billing_details': { 'name': 'Jenny Rosen', }, }, 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'); $intent = \Stripe\PaymentIntent::create([ 'confirm' => true, 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['bancontact'], 'payment_method_data' => [ 'type' => 'bancontact', 'billing_details' => [ 'name' => 'Jenny Rosen', ], ], '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"; ArrayList<String> paymentMethodTypes = new ArrayList<String>(); paymentMethodTypes.add("bancontact"); Map<String, Object> billingDetailsMap = new HashMap<>(); billingDetailsMap.put("name", "Jenny Rosen"); Map<String, Object> paymentMethodData = new HashMap<>(); paymentMethodData.put("type", "bancontact"); paymentMethodData.put("billing_details", billingDetailsMap); PaymentIntentCreateParams createParams = PaymentIntentCreateParams.builder() .setConfirm(true) .setAmount(1099) .setCurrency("eur") .setPaymentMethodTypes(paymentMethodTypes) .setPaymentMethodData(paymentMethodData) .setReturnURL("https://your-website.com/checkout/complete") .build(); PaymentIntent intent = PaymentIntent.create(createParams);
// 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 intent = await stripe.paymentIntents.create({ confirm: true, amount: 1099, currency: 'eur', payment_method_types: ['bancontact'], payment_method_data: { type: 'bancontact', billing_details: { name: 'Jenny Rosen', }, }, 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.PaymentIntentParams{ Confirm: stripe.Bool(true), Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyEUR)), PaymentMethodTypes: stripe.StringSlice([]string{ "bancontact", }), ReturnURL: stripe.String("https://your-website.com/checkout/complete"), } params.AddExtra("payment_method_data[type]", "bancontact") params.AddExtra("payment_method_data[billing_details][name]", "Jenny Rosen") 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 { Confirm = true, Amount = 1099, Currency = "eur", PaymentMethodTypes = new List<string> { "bancontact", }, ReturnUrl = "https://your-website.com/checkout/complete", }; options.AddExtraParam("payment_method_data[type]", "bancontact"); options.AddExtraParam("payment_method_data[billing_details][name]", "Jenny Rosen"); var service = new PaymentIntentService(); var paymentIntent = service.Create(options);
  1. Check that the PaymentIntent has a status of requires_action and the type for next_action is redirect_to_url.
{ "status": "requires_action", "next_action": { "type": "redirect_to_url", "redirect_to_url": { "url": "https://hooks.stripe.com/...", "return_url": "https://your-website.com/checkout/complete" } }, "id": "pi_1G1sgdKi6xqXeNtkldRRE6HT", "object": "payment_intent", "amount": 1099,
See all 30 lines "client_secret": "pi_1G1sgdKi6xqXeNtkldRRE6HT_secret_h9B56ObhTN72fQiBAuzcVPb2E", "confirmation_method": "automatic", "created": 1579259303, "currency": "eur", "livemode": true, "charges": { "data": [], "object": "list", "has_more": false, "url": "/v1/charges?payment_intent=pi_1G1sgdKi6xqXeNtkldRRE6HT" }, "payment_method_options": { "bancontact": {} }, "payment_method_types": [ "bancontact" ] }
  1. Redirect the customer to the URL provided in the next_action.redirect_to_url.url property. The code example here is approximate—the redirect method may be different in your web framework.
if payment_intent.status == 'requires_action' && payment_intent.next_action.type == 'redirect_to_url' url = payment_intent.next_action.redirect_to_url.url redirect(url) end
if payment_intent.status == 'requires_action' and payment_intent.next_action.type == 'redirect_to_url': url = payment_intent.next_action.redirect_to_url.url redirect(url)
if ($payment_intent->status == 'requires_action' && $payment_intent->next_action->type == 'redirect_to_url') { $url = $payment_intent->next_action->redirect_to_url->url Redirect::to($url) }
if (paymentIntent.status.equals('requires_action') && paymentIntent.next_action.type.equals('redirect_to_url')) { String url = paymentIntent.next_action.redirect_to_url.url; response.redirect(url); }
if (paymentIntent.status === 'requires_action' && paymentIntent.next_action.type === 'redirect_to_url') { const url = paymentIntent.next_action.redirect_to_url.url; res.redirect(url); }
if (paymentIntent.status == 'requires_action' && paymentIntent.next_action.type == 'redirect_to_url') { url := paymentIntent.next_action.redirect_to_url.url http.Redirect(url) }
if (paymentIntent.status == 'requires_action' && paymentIntent.next_action.type == 'redirect_to_url') { string url = payment_intent.next_action.redirect_to_url.url; Response.Redirect(url); }

When the customer finishes the payment process, they are sent to the return_url configured in step 1. 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.

We recommend that you rely on webhooks to confirm the status of a payment.

Accepting Bancontact payments in your app 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 Bancontact 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 { StripeAPI.defaultPublishableKey = "pk_test_TYooMQauvdEDq54NiTphI7jx" // do any other necessary launch configuration return true } }
#import "AppDelegate.h" @import Stripe; @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [StripeAPI setDefaultPublishableKey:@"pk_test_TYooMQauvdEDq54NiTphI7jx"]; // do any other necessary launch configuration return YES; } @end

2 Create or retrieve a Customer Server-side

To use a SEPA Direct Debit PaymentMethod for future payments, it must be attached to a Customer.

You should create a Customer object when your customer creates an account with your business. Associating the ID of the Customer object with your own internal representation of a customer will enable you to retrieve and use the stored payment method details later. If your customer is making a payment as a guest, you can still create a Customer object before payment and associate it with your internal representation of the customer’s account later.

When you include customer in your 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 (Bancontact doesn’t support other currencies). If you already have an integration using the Payment Intents API, add bancontact 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[]"=bancontact \ -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: ['bancontact'], 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=['bancontact'], 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' => ['bancontact'], '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("bancontact") .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: ['bancontact'], 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{ "bancontact", }), 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> { "bancontact", }, 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 and email address. Create an STPPaymentMethodParams object with these details.

let bancontactParams = STPPaymentMethodBancontactParams() let billingDetails = STPPaymentMethodBillingDetails() billingDetails.name = "Jane Doe" billingDetails.email = "jane.doe@example.com" let paymentMethodParams = STPPaymentMethodParams(bancontact: bancontactParams, billingDetails: billingDetails, metadata: nil)
STPPaymentMethodBancontactParams *bancontactParams = [[STPPaymentMethodBancontactParams alloc] init]; STPPaymentMethodBillingDetails *billingDetails = [[STPPaymentMethodBillingDetails alloc] init]; billingDetails.name = @"Jane Doe"; billingDetails.email = @"jane.doe@example.com"; STPPaymentMethodParams *paymentMethodParams = [STPPaymentMethodParams paramsWithBancontact:bancontactParams 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 Bancontact payment can be found on the Bancontact PaymentIntent’s successful Charge, under the payment_method_details property.

{ "charges": { "data": [ { "payment_method_details": { "bancontact": { "bank_code": "VAPE", "bank_name": "VAN DE PUT & CO", "bics": "VAPEBE22", "iban_last4": "7061", "generated_sepa_debit": "pm_1GrddXGf98efjktuBIi3ag7aJQ", "preferred_language": "en", "verified_name": "Jenny Rosen" }, "type": "bancontact"
See all 51 lines }, "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": "bancontact", "usage": "single_use" } ], "object": "list", "has_more": false, "url": "/v1/charges?payment_intent=pi_1G1sgdKi6xqXeNtkldRRE6HT" }, "payment_method_options": { "bancontact": {} }, "payment_method_types": [ "bancontact" ], "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

When you create a Bancontact 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 Bancontact PaymentIntent. PaymentIntents confirmed with the generated SEPA Direct Debit PaymentMethod will follow the behavior described below.

When confirming your Bancontact 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 Bancontact 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_bancontact_generatedSepaDebitIntentsSucceed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
pm_bancontact_generatedSepaDebitIntentsSucceedDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
pm_bancontact_generatedSepaDebitIntentsFail The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
pm_bancontact_generatedSepaDebitIntentsFailDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
pm_bancontact_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 Bancontact 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 Bancontact 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.1.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 (Bancontact doesn’t support other currencies). If you already have an integration using the Payment Intents API, add bancontact 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[]"=bancontact \ -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: ['bancontact'], 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=['bancontact'], 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' => ['bancontact'], '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("bancontact") .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: ['bancontact'], 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{ "bancontact", }), 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> { "bancontact", }, 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 and email address. Create a PaymentMethodCreateParams object with these details.

val billingDetails = PaymentMethod.BillingDetails(name = "Jenny Rosen", email = "jenny.rosen@example.com") val paymentMethodCreateParams = PaymentMethodCreateParams.createBancontact(billingDetails)
PaymentMethod.BillingDetails billingDetails = new PaymentMethod.BillingDetails.Builder() .setName("Jenny Rosen") .setEmail("jenny.rosen@example.com") .build(); PaymentMethodCreateParams paymentMethodCreateParams = PaymentMethodCreateParams.createBancontact(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 BancontactPaymentActivity : 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 BancontactPaymentActivity 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 Bancontact payment can be found on the Bancontact PaymentIntent’s successful Charge, under the payment_method_details property.

{ "charges": { "data": [ { "payment_method_details": { "bancontact": { "bank_code": "VAPE", "bank_name": "VAN DE PUT & CO", "bics": "VAPEBE22", "iban_last4": "7061", "generated_sepa_debit": "pm_1GrddXGf98efjktuBIi3ag7aJQ", "preferred_language": "en", "verified_name": "Jenny Rosen" }, "type": "bancontact"
See all 51 lines }, "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": "bancontact", "usage": "single_use" } ], "object": "list", "has_more": false, "url": "/v1/charges?payment_intent=pi_1G1sgdKi6xqXeNtkldRRE6HT" }, "payment_method_options": { "bancontact": {} }, "payment_method_types": [ "bancontact" ], "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

When you create a Bancontact 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 Bancontact PaymentIntent. PaymentIntents confirmed with the generated SEPA Direct Debit PaymentMethod will follow the behavior described below.

When confirming your Bancontact 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 Bancontact 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_bancontact_generatedSepaDebitIntentsSucceed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded.
pm_bancontact_generatedSepaDebitIntentsSucceedDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to succeeded after three minutes.
pm_bancontact_generatedSepaDebitIntentsFail The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method.
pm_bancontact_generatedSepaDebitIntentsFailDelayed The SEPA Direct Debit PaymentIntent status transitions from processing to requires_payment_method after three minutes.
pm_bancontact_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.