Create account
Sign in
Home
Payments
Business operations
Financial services
Developer tools
Security
All products
Home
Payments
Business operations
Home
Payments
Business operations
Financial services
Developer tools
Support
Overview
Overview
Bank debits
Bank redirects
Bancontact
EPS
FPX
giropay
iDEAL
Przelewy24
Sofort
Accept a payment
Save bank details during payment
Set up future payments
Bank transfers
Buy now pay later
Vouchers
Wallets
Testing
HomePaymentsBank redirectsSofort

Sofort payments

Learn how to accept Sofort, a common payment method in Europe.

Custom payment form Prebuilt checkout page iOS Android

Sofort with Sources

Looking to use the Sources API to accept Sofort? See the Sofort payments with Sources guide.

Sofort is a single use payment method where customers are required to authenticate their payment. Customers pay with Sofort by redirecting from your website, authorizing the payment, then returning to your website. It typically takes 2 to 14 days for you to receive notification of success or failure.

To accept Sofort, you must comply with our Sofort Terms of Service.

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:

Ruby Python PHP Java Node Go .NET
Terminal
# Available as a gem gem install stripe
# Available as a gem gem install stripe
Gemfile
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
Terminal
# Install through pip pip install --upgrade stripe
# Install through pip pip install --upgrade stripe
PyPI
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
requirements.txt
# 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
# 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
Terminal
# Install the PHP library via Composer composer require stripe/stripe-php
# Install the PHP library via Composer composer require stripe/stripe-php
Source
# Or download the source directly: https://github.com/stripe/stripe-php/releases
# Or download the source directly: https://github.com/stripe/stripe-php/releases
build.gradle
/* 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 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}"
pom.xml
<!-- 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 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>
Other environments
# 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
# 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
Terminal
# Install via npm npm install --save stripe
# Install via npm npm install --save stripe
Terminal
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
app.go
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
Terminal
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Install via dotnet dotnet add package Stripe.net dotnet restore
Terminal
# Or install via NuGet PM> Install-Package Stripe.net
# Or install via NuGet PM> Install-Package Stripe.net

2 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. If you already have an integration using the Payment Intents API, add sofort to the list of payment method types for your PaymentIntent.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], })
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], })
# 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( amount=1099, currency='eur', payment_method_types=['sofort'] )
# 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( amount=1099, currency='eur', payment_method_types=['sofort'] )
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], ]);
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], ]);
// 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("sofort") .build(); 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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("eur") .addPaymentMethodType("sofort") .build(); 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], });
// 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{ "sofort", }), } 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyEUR)), PaymentMethodTypes: stripe.StringSlice([]string{ "sofort", }), } 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> { "sofort", }, }; var service = new PaymentIntentService(); var intent = service.Create(options);
// 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> { "sofort", }, }; var service = new PaymentIntentService(); var intent = service.Create(options);

Changing the preferred language

By default, Stripe presents the Sofort authorization page in a language based on the specified country code. You can customize this to the language preferred by your customer by specifying it as part of the request and changing the value of the preferred_language property. The supported values are de, en, es, it, fr, nl, and pl.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort \ -d "payment_method_options[sofort][preferred_language]"=de
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort \ -d "payment_method_options[sofort][preferred_language]"=de
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, })∂
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, })∂
# 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( amount=1099, currency='eur', payment_method_types=['sofort'], payment_method_options={ 'sofort': { 'preferred_language': 'de', }, }, )
# 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( amount=1099, currency='eur', payment_method_types=['sofort'], payment_method_options={ 'sofort': { 'preferred_language': 'de', }, }, )
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], 'payment_method_options' => [ 'sofort' => [ 'preferred_language' => 'de', ], ], ]);
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], 'payment_method_options' => [ 'sofort' => [ 'preferred_language' => 'de', ], ], ]);
// 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("sofort") .setPaymentMethodOptions( PaymentIntentCreateParams.PaymentMethodOptions.builder() .putExtraParam("sofort[preferred_language]", "de") .build()) .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("eur") .addPaymentMethodType("sofort") .setPaymentMethodOptions( PaymentIntentCreateParams.PaymentMethodOptions.builder() .putExtraParam("sofort[preferred_language]", "de") .build()) .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, });
// 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{ "sofort", }), } params.AddExtra("payment_method_options[sofort][preferred_language]", "de") 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyEUR)), PaymentMethodTypes: stripe.StringSlice([]string{ "sofort", }), } params.AddExtra("payment_method_options[sofort][preferred_language]", "de") 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> { "sofort", }, }; options.AddExtraParam("payment_method_options[sofort][preferred_language]", "de"); var service = new PaymentIntentService(); var intent = service.Create(options);
// 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> { "sofort", }, }; options.AddExtraParam("payment_method_options[sofort][preferred_language]", "de"); var service = new PaymentIntentService(); var intent = service.Create(options);

3 Submit the payment to Stripe Client-side

Confirm Sofort payment

To complete the payment on the client side, provide your client application with the client secret of the PaymentIntent object that you created in Step 2.

HTML + JS React

When a customer clicks to pay with Sofort, we recommend you use Stripe.js to submit the payment to Stripe. Stripe.js is our foundational JavaScript library for building payment flows. It will automatically handle complexities like the redirect described below, and enables you to easily extend your integration to other payment methods in the future.

Include the Stripe.js script on your checkout page by adding it to the head of your HTML file.

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

Create an instance of Stripe.js with the following JavaScript on your checkout page.

JavaScript JavaScript (ESNext)
client.js
// Set your publishable key. Remember to change this to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
// Set your publishable key. Remember to change this to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
client.js
// Set your publishable key. Remember to change this to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
// Set your publishable key. Remember to change this to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');

Use stripe.confirmSofortPayment 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 the Sofort website or mobile application.

JavaScript JavaScript (ESNext)
client.js
// Redirects away from the client stripe.confirmSofortPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { sofort: { country: "DE" } }, return_url: 'https://your-website.com/checkout/complete', } );
// Redirects away from the client stripe.confirmSofortPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { sofort: { country: "DE" } }, return_url: 'https://your-website.com/checkout/complete', } );
client.js
// Redirects away from the client const {error} = await stripe.confirmSofortPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { sofort: { country: "DE" } }, return_url: 'https://your-website.com/checkout/complete', } );
// Redirects away from the client const {error} = await stripe.confirmSofortPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { sofort: { country: "DE" } }, return_url: 'https://your-website.com/checkout/complete', } );
npm umd

Install React Stripe.js and the Stripe.js loader from the npm public registry.

Terminal
npm install --save @stripe/react-stripe-js @stripe/stripe-js
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>
<!-- 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>

The demo in CodeSandbox lets you try out React Stripe.js without having to create a new project.

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.

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

Use stripe.confirmSofortPayment 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.confirmSofortPayment 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.

Hooks Class Components
CheckoutForm.js
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 {error} = await stripe.confirmSofortPayment('{CLIENT_SECRET}', { payment_method: { sofort: { country: "DE" }, billing_details: { name: accountholderName.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> <button type="submit" disabled={!stripe}> Submit Payment </button> </form> ); }
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 {error} = await stripe.confirmSofortPayment('{CLIENT_SECRET}', { payment_method: { sofort: { country: "DE" }, billing_details: { name: accountholderName.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> <button type="submit" disabled={!stripe}> Submit Payment </button> </form> ); }
CheckoutForm.js
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 {error} = await stripe.confirmSofortPayment('{CLIENT_SECRET}', { payment_method: { sofort: { country: "DE" }, billing_details: { name: accountholderName.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> <button type="submit" disabled={!stripe}> Submit Payment </button> </form> ); } } export default function InjectedCheckoutForm() { return ( <ElementsConsumer> {({stripe, elements}) => ( <CheckoutForm stripe={stripe} elements={elements} /> )} </ElementsConsumer> ); }
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 {error} = await stripe.confirmSofortPayment('{CLIENT_SECRET}', { payment_method: { sofort: { country: "DE" }, billing_details: { name: accountholderName.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> <button type="submit" disabled={!stripe}> Submit Payment </button> </form> ); } } export default function InjectedCheckoutForm() { return ( <ElementsConsumer> {({stripe, elements}) => ( <CheckoutForm stripe={stripe} elements={elements} /> )} </ElementsConsumer> ); }

Handling the redirect

The following URL query parameters are provided when Stripe redirects the customer to the return_url.

Parameter Description
payment_intent The unique identifier for the PaymentIntent.
payment_intent_client_secret The client secret of the PaymentIntent object.

You may also append your own query parameters when providing the return_url. They will persist through the redirect process.

The return_url should correspond to a page on your website that provides the status of the payment. You should verify the status of the PaymentIntent when rendering the return page. You can do so by using the retrievePaymentIntent function from Stripe.js and passing in the payment_intent_client_secret.

JavaScript JavaScript (ESNext)
var url = new URL(window.location); var clientSecret = url.searchParams.get('payment_intent_client_secret'); stripe.retrievePaymentIntent(clientSecret).then(function(response) { if (response.error) { // Handle error here } else if (response.paymentIntent && response.paymentIntent.status === 'succeeded') { // Handle successful payment here } });
var url = new URL(window.location); var clientSecret = url.searchParams.get('payment_intent_client_secret'); stripe.retrievePaymentIntent(clientSecret).then(function(response) { if (response.error) { // Handle error here } else if (response.paymentIntent && response.paymentIntent.status === 'succeeded') { // Handle successful payment here } });
(async () => { const url = new URL(window.location); const clientSecret = url.searchParams.get('payment_intent_client_secret'); const {paymentIntent, error} = await stripe.retrievePaymentIntent(clientSecret); if (error) { // Handle error here } else if (paymentIntent && paymentIntent.status === 'succeeded') { // Handle successful payment here } })();
(async () => { const url = new URL(window.location); const clientSecret = url.searchParams.get('payment_intent_client_secret'); const {paymentIntent, error} = await stripe.retrievePaymentIntent(clientSecret); if (error) { // Handle error here } else if (paymentIntent && paymentIntent.status === 'succeeded') { // Handle successful payment here } })();

You can find details about the bank account the customer used to complete the payment on the resulting Charge under the payment_method_details property.

"payment_method_details": { "sofort": { "bank_code": "VAPE", "bank_name": "VAN DE PUT & CO", "bics": "VAPEBE22", "country": "DE", "iban_last4": "7061", "preferred_language": "en", "verified_name": "Jenny Rosen", }, "type": "sofort" }
"payment_method_details": { "sofort": { "bank_code": "VAPE", "bank_name": "VAN DE PUT & CO", "bics": "VAPEBE22", "country": "DE", "iban_last4": "7061", "preferred_language": "en", "verified_name": "Jenny Rosen", }, "type": "sofort" }

Mobile applications

To integrate Sofort within a mobile application, provide your application URI scheme as the return_url value so that your customers are returned to your app after completing authorization. Refer to our payments documentation for iOS or Android to learn more.

If you’re integrating without using our mobile SDKs, the redirect URL must be opened using the device’s native browser. The use of in-app web views and containers can prevent your customer from completing authentication—resulting in a lower conversion rate.

Testing the redirect process

When creating a PaymentIntent object using your test API keys, you’ll be redirected to a Stripe page that displays information about the API request and you can either authorize or cancel the payment. Authorizing the payment redirects you to the URL specified in return_url.

Handle post-payment events

Because Sofort is a delayed notification payment method, the PaymentIntent object’s status remains in a payment_intent.processing state for up to 14 days from its creation (also known as the cut-off date). In test mode, the PaymentIntent object’s status will remain in a payment_intent.processing state for three minutes to simulate this.

We recommend that you fulfill purchases made with Sofort when the payment intent enters the payment_intent.processing state. On average, you can expect approximately 0.2% of Sofort payment attempts to fail after entering the payment_intent.processing state. You may prefer to fulfill orders only after receiving the payment_intent.succeeded webhook. Note that this guidance only applies to Sofort payments due to its low payment failure rate and does not apply to other delayed notification payment methods.

After the payment attempt is confirmed—and the funds guaranteed—its status updates to payment_intent.succeeded.

If a customer doesn’t pay, the PaymentIntent emits the payment_intent.failed webhook event and returns to a status of requires_payment_method.

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.

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.

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.

View your test payments in the Dashboard

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.

Build a custom webhook

Prebuilt apps

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

Browse shipping apps and extensions

Browse inventory management apps and extensions

See all partner solutions

Optional Handle the Sofort redirect manually

We recommend relying on Stripe.js to handle Sofort redirects and payments client-side with confirmSofortPayment. 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 sofort. You must provide the payment_method_data.sofort.country property as it’s required by Sofort. Note that by specifying payment_method_data, we’ll create a PaymentMethod and immediately use it with this PaymentIntent.

    You must also provide the redirect URL for your customer after they complete their payment in the return_url field. You can also provide your own query parameters in this URL. These parameters are included in the final URL upon completing the redirect flow.
curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d confirm=true \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort \ -d "payment_method_data[type]"=sofort \ -d "payment_method_data[sofort][country]"=DE \ -d return_url="https://your-website.com/checkout/complete"
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d confirm=true \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort \ -d "payment_method_data[type]"=sofort \ -d "payment_method_data[sofort][country]"=DE \ -d return_url="https://your-website.com/checkout/complete"
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' payment_intent = Stripe::PaymentIntent.create({ confirm: true, amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_data: { type: 'sofort', sofort: { country: 'DE', }, }, 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' payment_intent = Stripe::PaymentIntent.create({ confirm: true, amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_data: { type: 'sofort', sofort: { country: 'DE', }, }, 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=['sofort'], payment_method_data={ 'type': 'sofort', 'sofort': { 'country': 'DE', }, }, 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=['sofort'], payment_method_data={ 'type': 'sofort', 'sofort': { 'country': 'DE', }, }, 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' => ['sofort'], 'payment_method_data' => [ 'type' => 'sofort', 'sofort' => [ 'country' => 'DE', ], ], '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' => ['sofort'], 'payment_method_data' => [ 'type' => 'sofort', 'sofort' => [ 'country' => 'DE', ], ], '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"; PaymentIntentCreateParams createParams = PaymentIntentCreateParams.builder() .setConfirm(true) .setAmount(1099L) .setCurrency("eur") .addPaymentMethodType("sofort") .setReturnUrl("https://your-website.com/checkout/complete") .putExtraParam("payment_method_data[type]", "sofort") .putExtraParam("payment_method_data[sofort][country]", "DE") .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams createParams = PaymentIntentCreateParams.builder() .setConfirm(true) .setAmount(1099L) .setCurrency("eur") .addPaymentMethodType("sofort") .setReturnUrl("https://your-website.com/checkout/complete") .putExtraParam("payment_method_data[type]", "sofort") .putExtraParam("payment_method_data[sofort][country]", "DE") .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const intent = await stripe.paymentIntents.create({ confirm: true, amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_data: { type: 'sofort', sofort: { country: 'DE', }, }, 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 const Stripe = require('stripe'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const intent = await stripe.paymentIntents.create({ confirm: true, amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_data: { type: 'sofort', sofort: { country: 'DE', }, }, 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{ "sofort", }), ReturnURL: stripe.String("https://your-website.com/checkout/complete"), } params.AddExtra("payment_method_data[type]", "sofort") params.AddExtra("payment_method_data[sofort][country]", "DE") 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 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{ "sofort", }), ReturnURL: stripe.String("https://your-website.com/checkout/complete"), } params.AddExtra("payment_method_data[type]", "sofort") params.AddExtra("payment_method_data[sofort][country]", "DE") 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> { "sofort", }, ReturnUrl = "https://your-website.com/checkout/complete", }; options.AddExtraParam("payment_method_data[type]", "sofort"); options.AddExtraParam("payment_method_data[sofort][country]", "DE"); var paymentIntentService = new PaymentIntentService(); var paymentIntent = paymentIntentService.Create(options);
// 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> { "sofort", }, ReturnUrl = "https://your-website.com/checkout/complete", }; options.AddExtraParam("payment_method_data[type]", "sofort"); options.AddExtraParam("payment_method_data[sofort][country]", "DE"); var paymentIntentService = new PaymentIntentService(); var paymentIntent = paymentIntentService.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, "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": { "sofort": {} }, "payment_method_types": [ "sofort" ] }
{ "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": { "sofort": {} }, "payment_method_types": [ "sofort" ] }
  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.
Ruby Python PHP Java Node Go .NET
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' && 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' 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 ($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.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') { 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') { 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); }
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.

Disputed payments

The risk of fraud or unrecognized payments is low because the customer must authenticate the payment with their bank. As a result, you won’t have disputes that turn into chargebacks, with funds withdrawn from your Stripe account.

Failed attempts

If a payment attempt hasn’t been confirmed within the cut-off time, the PaymentIntent object’s status automatically transitions from processing to requires_payment_method. Additionally, if the funds are received after the cut-off date, the customer is automatically refunded.

On average, you can expect approximately 0.2% of Sofort payment attempts to fail. This may vary based on your industry or customer base. Depending on your average payment amount, the type of products or service you provide, and the risk associated with your business, you may prefer to fulfill orders only after receiving the payment_intent.succeeded webhook.

Refunds

Sofort only accepts refund requests within 180 days from the date of the original payment. After 180 days, it’s no longer possible to refund the payment.

You can submit a refund against pending charges that haven’t been confirmed yet. If you create a full or partial refund on a processing PaymentIntent, the refund occurs only after the PaymentIntent’s status is transitioned to succeeded. If the PaymentIntent object transitions to status requires_payment_method after a payment attempt failure, full and partial refunds will be marked as canceled, as the money never left the customer’s bank account.

Customers pay with Sofort by redirecting away from the Checkout Session to their bank, sending you payment, and then returning to Checkout. They are then redirected back to your site.

SOFORT is a delayed notification payment method, which means that funds are not immediately available after payment. A payment typically takes 2 to 14 business days to arrive in your account.

1 Determine compatibility

Supported business locations: Europe, US, CA, NZ, SG, HK, JP, AU, MX

Supported currencies: eur

Presentment currencies: eur

Payment mode: Yes

Setup mode: No

Subscription mode: No

A Checkout Session must satisfy all of the following conditions to support SOFORT payments:

  • You can only use one-time line items (recurring subscription plans are not supported).
  • Prices for all line items must be expressed in Euro (currency code eur).

2 Accept a payment

You should first build an integration to accept a payment with Checkout before using this guide.

This guide walks you through enabling SOFORT and shows the differences between accepting a card payment and using SOFORT.

Enable SOFORT as a payment method

When creating a new Checkout Session, you need to:

  1. Add sofort to the list of payment_method_types.
  2. Make sure all your line_items use the eur currency.
Ruby Python PHP Java Node Go .NET
Stripe::Checkout::Session.create({ mode: 'payment', payment_method_types: ['card'], payment_method_types: ['sofort'], # or you can take multiple payment methods with # payment_method_types: ['card', 'sofort', ...] line_items: [{ price_data: { currency: 'usd', # To accept `sofort`, all line items must have currency: `eur` currency: 'eur', product_data: { name: 'T-shirt', }, unit_amount: 2000, }, quantity: 1, }], success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', })
Stripe::Checkout::Session.create({ mode: 'payment', payment_method_types: ['card'], payment_method_types: ['sofort'], # or you can take multiple payment methods with # payment_method_types: ['card', 'sofort', ...] line_items: [{ price_data: { currency: 'usd', # To accept `sofort`, all line items must have currency: `eur` currency: 'eur', product_data: { name: 'T-shirt', }, unit_amount: 2000, }, quantity: 1, }], success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', })
stripe.checkout.Session.create( payment_method_types=['card'], payment_method_types=['sofort'], # or you can take multiple payment methods with # payment_method_types=['card', 'sofort', ...] line_items=[{ 'price_data': { currency: 'usd', # To accept `sofort`, all line items must have currency: `eur` currency: 'eur', 'product_data': { 'name': 'T-shirt', }, 'unit_amount': 2000, }, 'quantity': 1, }], mode='payment', success_url='https://example.com/success', cancel_url='https://example.com/cancel', )
stripe.checkout.Session.create( payment_method_types=['card'], payment_method_types=['sofort'], # or you can take multiple payment methods with # payment_method_types=['card', 'sofort', ...] line_items=[{ 'price_data': { currency: 'usd', # To accept `sofort`, all line items must have currency: `eur` currency: 'eur', 'product_data': { 'name': 'T-shirt', }, 'unit_amount': 2000, }, 'quantity': 1, }], mode='payment', success_url='https://example.com/success', cancel_url='https://example.com/cancel', )
$session = \Stripe\Checkout\Session::create([ 'payment_method_types' => ['card'], 'payment_method_types' => ['sofort'], // or you can take multiple payment methods with // 'payment_method_types' => ['card', 'sofort', ...] 'line_items' => [[ 'price_data' => [ 'currency' => 'usd', # To accept `sofort`, all line items must have currency: `eur` 'currency' => 'eur', 'product_data' => [ 'name' => 'T-shirt', ], 'unit_amount' => 2000, ], 'quantity' => 1, ]], 'mode' => 'payment', 'success_url' => 'https://example.com/success', 'cancel_url' => 'https://example.com/cancel', ]);
$session = \Stripe\Checkout\Session::create([ 'payment_method_types' => ['card'], 'payment_method_types' => ['sofort'], // or you can take multiple payment methods with // 'payment_method_types' => ['card', 'sofort', ...] 'line_items' => [[ 'price_data' => [ 'currency' => 'usd', # To accept `sofort`, all line items must have currency: `eur` 'currency' => 'eur', 'product_data' => [ 'name' => 'T-shirt', ], 'unit_amount' => 2000, ], 'quantity' => 1, ]], 'mode' => 'payment', 'success_url' => 'https://example.com/success', 'cancel_url' => 'https://example.com/cancel', ]);
SessionCreateParams params = SessionCreateParams.builder() .addPaymentMethodType(SessionCreateParams.PaymentMethodType.CARD) .addPaymentMethodType(SessionCreateParams.PaymentMethodType.SOFORT) .addLineItem( SessionCreateParams.LineItem.builder() .setPriceData( SessionCreateParams.LineItem.PriceData.builder() .setCurrency("usd") // To accept `sofort`, all line items must have currency: `eur` .setCurrency("eur") .setUnitAmount(2000L) .setProductData( SessionCreateParams.LineItem.PriceData.ProductData.builder() .setName("T-shirt") .build()) .build()) .setQuantity(1L) .build()) .setMode(SessionCreateParams.Mode.PAYMENT) .setSuccessUrl("https://example.com/success") .setCancelUrl("https://example.com/cancel") .build(); Session session = Session.create(params);
SessionCreateParams params = SessionCreateParams.builder() .addPaymentMethodType(SessionCreateParams.PaymentMethodType.CARD) .addPaymentMethodType(SessionCreateParams.PaymentMethodType.SOFORT) .addLineItem( SessionCreateParams.LineItem.builder() .setPriceData( SessionCreateParams.LineItem.PriceData.builder() .setCurrency("usd") // To accept `sofort`, all line items must have currency: `eur` .setCurrency("eur") .setUnitAmount(2000L) .setProductData( SessionCreateParams.LineItem.PriceData.ProductData.builder() .setName("T-shirt") .build()) .build()) .setQuantity(1L) .build()) .setMode(SessionCreateParams.Mode.PAYMENT) .setSuccessUrl("https://example.com/success") .setCancelUrl("https://example.com/cancel") .build(); Session session = Session.create(params);
const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], payment_method_types: ['sofort'], // or you can take multiple payment methods with // payment_method_types: ['card', 'sofort', ...] line_items: [{ price_data: { currency: 'usd', // To accept `sofort`, all line items must have currency: `eur` currency: 'eur', product_data: { name: 'T-shirt', }, unit_amount: 2000, }, quantity: 1, }], mode: 'payment', success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', });
const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], payment_method_types: ['sofort'], // or you can take multiple payment methods with // payment_method_types: ['card', 'sofort', ...] line_items: [{ price_data: { currency: 'usd', // To accept `sofort`, all line items must have currency: `eur` currency: 'eur', product_data: { name: 'T-shirt', }, unit_amount: 2000, }, quantity: 1, }], mode: 'payment', success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', });
params := &stripe.CheckoutSessionParams{ PaymentMethodTypes: stripe.StringSlice([]string{ "card", "sofort", }), LineItems: []*stripe.CheckoutSessionLineItemParams{ &stripe.CheckoutSessionLineItemParams{ PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{ Currency: stripe.String("usd"), // To accept `sofort`, all line items must have currency: `eur` Currency: stripe.String("eur"), ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{ Name: stripe.String("T-shirt"), }, UnitAmount: stripe.Int64(2000), }, Quantity: stripe.Int64(1), }, }, Mode: stripe.String(string(stripe.CheckoutSessionModePayment)), SuccessURL: stripe.String("https://example.com/success"), CancelURL: stripe.String("https://example.com/cancel"), } s, _ := session.New(params)
params := &stripe.CheckoutSessionParams{ PaymentMethodTypes: stripe.StringSlice([]string{ "card", "sofort", }), LineItems: []*stripe.CheckoutSessionLineItemParams{ &stripe.CheckoutSessionLineItemParams{ PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{ Currency: stripe.String("usd"), // To accept `sofort`, all line items must have currency: `eur` Currency: stripe.String("eur"), ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{ Name: stripe.String("T-shirt"), }, UnitAmount: stripe.Int64(2000), }, Quantity: stripe.Int64(1), }, }, Mode: stripe.String(string(stripe.CheckoutSessionModePayment)), SuccessURL: stripe.String("https://example.com/success"), CancelURL: stripe.String("https://example.com/cancel"), } s, _ := session.New(params)
var options = new SessionCreateOptions { PaymentMethodTypes = new List<string> { "card", "sofort", }, LineItems = new List<SessionLineItemOptions> { new SessionLineItemOptions { PriceData = new SessionLineItemPriceDataOptions { UnitAmount = 2000, Currency = "usd", // To accept `sofort`, all line items must have currency: `eur` Currency = "eur", ProductData = new SessionLineItemPriceDataProductDataOptions { Name = "T-shirt", }, }, Quantity = 1, }, }, Mode = "payment", SuccessUrl = "https://example.com/success", CancelUrl = "https://example.com/cancel", }; var service = new SessionService(); var session = service.Create(options);
var options = new SessionCreateOptions { PaymentMethodTypes = new List<string> { "card", "sofort", }, LineItems = new List<SessionLineItemOptions> { new SessionLineItemOptions { PriceData = new SessionLineItemPriceDataOptions { UnitAmount = 2000, Currency = "usd", // To accept `sofort`, all line items must have currency: `eur` Currency = "eur", ProductData = new SessionLineItemPriceDataProductDataOptions { Name = "T-shirt", }, }, Quantity = 1, }, }, Mode = "payment", SuccessUrl = "https://example.com/success", CancelUrl = "https://example.com/cancel", }; var service = new SessionService(); var session = service.Create(options);

Fulfill your orders

After accepting a payment, learn how to fulfill orders.

Because SOFORT is a delayed notification payment method, you must also complete the handle delayed notification payment methods step of the guide.

3 Test your integration

When testing your Checkout integration, select SOFORT as the payment method and click the Pay button.

4 Handle refunds and disputes

The refund period for SOFORT is up to 180 days after the original payment.

There is no dispute process–customers authenticate with their bank.

Accepting Sofort in your app sends your customer to the Sofort online portal to authorize the payment. It typically takes 2 to 14 days for you to receive a notification of success or failure.

To accept Sofort, you must comply with our Sofort Terms of Service.

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:

Ruby Python PHP Java Node Go .NET
Terminal
# Available as a gem gem install stripe
# Available as a gem gem install stripe
Gemfile
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
Terminal
# Install through pip pip install --upgrade stripe
# Install through pip pip install --upgrade stripe
PyPI
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
requirements.txt
# 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
# 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
Terminal
# Install the PHP library via Composer composer require stripe/stripe-php
# Install the PHP library via Composer composer require stripe/stripe-php
Source
# Or download the source directly: https://github.com/stripe/stripe-php/releases
# Or download the source directly: https://github.com/stripe/stripe-php/releases
build.gradle
/* 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 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}"
pom.xml
<!-- 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 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>
Other environments
# 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
# 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
Terminal
# Install via npm npm install --save stripe
# Install via npm npm install --save stripe
Terminal
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
app.go
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
Terminal
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Install via dotnet dotnet add package Stripe.net dotnet restore
Terminal
# Or install via NuGet PM> Install-Package Stripe.net
# 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.

CocoaPods Carthage Dynamic Framework
  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:
    Terminal
    pod init
    pod init
  3. Add this line to your Podfile:
    Podfile
    pod 'Stripe'
    pod 'Stripe'
  4. Run the following command:
    Terminal
    pod install
    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:
    Terminal
    pod update Stripe
    pod update Stripe
  1. If you haven't already, install the latest version of Carthage.
  2. Add this line to your Cartfile:
    Cartfile
    github "stripe/stripe-ios"
    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:
    Terminal
    carthage update stripe-ios --platform ios
    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"
    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.

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.

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

Swift Objective-C
AppDelegate.swift
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 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 } }
AppDelegate.m
#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
#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

Use your test mode keys while you test and develop, and your live mode keys before you publish your app.

2 Create a PaymentIntent Server-side

Server-side

A PaymentIntent is an object that represents your intent to collect payment 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. If you already have an integration using the Payment Intents API, add sofort to the list of payment method types for your PaymentIntent.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], })
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], })
# 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( amount=1099, currency='eur', payment_method_types=['sofort'] )
# 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( amount=1099, currency='eur', payment_method_types=['sofort'] )
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], ]);
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], ]);
// 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("sofort") .build(); 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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("eur") .addPaymentMethodType("sofort") .build(); 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], });
// 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{ "sofort", }), } 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyEUR)), PaymentMethodTypes: stripe.StringSlice([]string{ "sofort", }), } 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> { "sofort", }, }; var service = new PaymentIntentService(); var intent = service.Create(options);
// 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> { "sofort", }, }; var service = new PaymentIntentService(); var intent = service.Create(options);

Changing the preferred language

By default, Stripe presents the Sofort authorization page in a language based on the specified country code. You can customize this to the language preferred by your customer by specifying it as part of the request and changing the value of the preferred_language property. The supported values are de, en, es, it, fr, nl, and pl.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort \ -d "payment_method_options[sofort][preferred_language]"=de
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort \ -d "payment_method_options[sofort][preferred_language]"=de
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, })∂
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, })∂
# 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( amount=1099, currency='eur', payment_method_types=['sofort'], payment_method_options={ 'sofort': { 'preferred_language': 'de', }, }, )
# 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( amount=1099, currency='eur', payment_method_types=['sofort'], payment_method_options={ 'sofort': { 'preferred_language': 'de', }, }, )
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], 'payment_method_options' => [ 'sofort' => [ 'preferred_language' => 'de', ], ], ]);
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], 'payment_method_options' => [ 'sofort' => [ 'preferred_language' => 'de', ], ], ]);
// 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("sofort") .setPaymentMethodOptions( PaymentIntentCreateParams.PaymentMethodOptions.builder() .putExtraParam("sofort[preferred_language]", "de") .build()) .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("eur") .addPaymentMethodType("sofort") .setPaymentMethodOptions( PaymentIntentCreateParams.PaymentMethodOptions.builder() .putExtraParam("sofort[preferred_language]", "de") .build()) .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, });
// 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{ "sofort", }), } params.AddExtra("payment_method_options[sofort][preferred_language]", "de") 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyEUR)), PaymentMethodTypes: stripe.StringSlice([]string{ "sofort", }), } params.AddExtra("payment_method_options[sofort][preferred_language]", "de") 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> { "sofort", }, }; options.AddExtraParam("payment_method_options[sofort][preferred_language]", "de"); var service = new PaymentIntentService(); var intent = service.Create(options);
// 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> { "sofort", }, }; options.AddExtraParam("payment_method_options[sofort][preferred_language]", "de"); var service = new PaymentIntentService(); var intent = service.Create(options);

Client-side

On the client, request a PaymentIntent from your server and store its client secret.

Swift Objective-C
CheckoutViewController.swift
View full sample
class CheckoutViewController: UIViewController { var paymentIntentClientSecret: String? func startCheckout() { // Request a PaymentIntent from your server and store its client secret } }
class CheckoutViewController: UIViewController { var paymentIntentClientSecret: String? func startCheckout() { // Request a PaymentIntent from your server and store its client secret } }
CheckoutViewController.m
View full sample
@interface CheckoutViewController () @property (copy, nonatomic) NSString *paymentIntentClientSecret; @end @implementation CheckoutViewController - (void)startCheckout { // Request a PaymentIntent from your server and store its client secret } @end
@interface CheckoutViewController () @property (copy, nonatomic) NSString *paymentIntentClientSecret; @end @implementation CheckoutViewController - (void)startCheckout { // Request a PaymentIntent from your server and store its client secret } @end

3 Submit the payment to Stripe Client-side

When a customer taps to pay with Sofort, confirm the PaymentIntent to complete the payment.

First, configure a STPPaymentIntentParams object with the PaymentIntent client secret from your server. Sofort requires that you collect the country code of the user’s bank and pass it in the STPPaymentMethodSofortParams.

Rather than sending the entire PaymentIntent object to the client, use its client secret. This is different from your API keys that authenticate Stripe API requests. The client secret is a string that lets your app access important fields from the PaymentIntent (e.g., status) while hiding sensitive ones (e.g., customer).

Set up a return URL

The iOS SDK can present a webview in your app to complete the Sofort payment. When authentication is finished, the webview can automatically dismiss itself instead of having your customer close it. To enable this behavior, configure a custom URL scheme or universal link and set up your app delegate to forward the URL to the SDK.

Swift Objective-C
// This method handles opening custom URL schemes (e.g., "your-app://") func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let stripeHandled = StripeAPI.handleURLCallback(with: url) if (stripeHandled) { return true } else { // This was not a Stripe url – handle the URL normally as you would } return false } // This method handles opening universal link URLs (e.g., "https://example.com/stripe_ios_callback") func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb { if let url = userActivity.webpageURL { let stripeHandled = StripeAPI.handleURLCallback(with: url) if (stripeHandled) { return true } else { // This was not a Stripe url – handle the URL normally as you would } } } return false }
// This method handles opening custom URL schemes (e.g., "your-app://") func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let stripeHandled = StripeAPI.handleURLCallback(with: url) if (stripeHandled) { return true } else { // This was not a Stripe url – handle the URL normally as you would } return false } // This method handles opening universal link URLs (e.g., "https://example.com/stripe_ios_callback") func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb { if let url = userActivity.webpageURL { let stripeHandled = StripeAPI.handleURLCallback(with: url) if (stripeHandled) { return true } else { // This was not a Stripe url – handle the URL normally as you would } } } return false }
// This method handles opening custom URL schemes (e.g., "your-app://") - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { BOOL stripeHandled = [StripeAPI handleStripeURLCallbackWithURL:url]; if (stripeHandled) { return YES; } else { // This was not a Stripe url – handle the URL normally as you would } return NO; } // This method handles opening universal link URLs (e.g., "https://example.com/stripe_ios_callback") - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler { if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { if (userActivity.webpageURL) { BOOL stripeHandled = [StripeAPI handleStripeURLCallbackWithURL:userActivity.webpageURL]; if (stripeHandled) { return YES; } else { // This was not a Stripe url – handle the URL normally as you would } return NO; } } return NO; }
// This method handles opening custom URL schemes (e.g., "your-app://") - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { BOOL stripeHandled = [StripeAPI handleStripeURLCallbackWithURL:url]; if (stripeHandled) { return YES; } else { // This was not a Stripe url – handle the URL normally as you would } return NO; } // This method handles opening universal link URLs (e.g., "https://example.com/stripe_ios_callback") - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler { if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { if (userActivity.webpageURL) { BOOL stripeHandled = [StripeAPI handleStripeURLCallbackWithURL:userActivity.webpageURL]; if (stripeHandled) { return YES; } else { // This was not a Stripe url – handle the URL normally as you would } return NO; } } return NO; }

Next, complete the payment by calling the STPPaymentHandler confirmPayment method.

Swift Objective-C
let paymentIntentParams = STPPaymentIntentParams(clientSecret: paymentIntentClientSecret) let sofort = STPPaymentMethodSofortParams() sofort.country = "DE" paymentIntentParams.paymentMethodParams = STPPaymentMethodParams(sofort: sofort, billingDetails: nil, metadata: nil) paymentIntentParams.returnURL = "payments-example://stripe-redirect" STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: self) { (handlerStatus, paymentIntent, error) in switch handlerStatus { case .succeeded: // Payment succeeded // ... case .canceled: // Payment canceled // ... case .failed: // Payment failed // ... @unknown default: fatalError() } }
let paymentIntentParams = STPPaymentIntentParams(clientSecret: paymentIntentClientSecret) let sofort = STPPaymentMethodSofortParams() sofort.country = "DE" paymentIntentParams.paymentMethodParams = STPPaymentMethodParams(sofort: sofort, billingDetails: nil, metadata: nil) paymentIntentParams.returnURL = "payments-example://stripe-redirect" STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: self) { (handlerStatus, paymentIntent, error) in switch handlerStatus { case .succeeded: // Payment succeeded // ... case .canceled: // Payment canceled // ... case .failed: // Payment failed // ... @unknown default: fatalError() } }
STPPaymentIntentParams *paymentIntentParams = [[STPPaymentIntentParams alloc] initWithClientSecret:clientSecret]; STPPaymentMethodSofortParams *sofort = [[STPPaymentMethodSofortParams alloc] init]; sofort.country = @"DE"; paymentIntentParams.paymentMethodParams = [STPPaymentMethodParams paramsWithSofort:sofort billingDetails:nil metadata:nil]; paymentIntentParams.returnURL = @"payments-example://stripe-redirect"; [[STPPaymentHandler sharedHandler] confirmPayment:paymentIntentParams withAuthenticationContext:self.delegate completion:^(STPPaymentHandlerActionStatus handlerStatus, STPPaymentIntent * handledIntent, NSError * _Nullable handlerError) { switch (handlerStatus) { case STPPaymentHandlerActionStatusFailed: // Payment failed // ... break; case STPPaymentHandlerActionStatusCanceled: // Payment canceled // ... break; case STPPaymentHandlerActionStatusSucceeded: // Payment succeeded // ... break; } }];
STPPaymentIntentParams *paymentIntentParams = [[STPPaymentIntentParams alloc] initWithClientSecret:clientSecret]; STPPaymentMethodSofortParams *sofort = [[STPPaymentMethodSofortParams alloc] init]; sofort.country = @"DE"; paymentIntentParams.paymentMethodParams = [STPPaymentMethodParams paramsWithSofort:sofort billingDetails:nil metadata:nil]; paymentIntentParams.returnURL = @"payments-example://stripe-redirect"; [[STPPaymentHandler sharedHandler] confirmPayment:paymentIntentParams withAuthenticationContext:self.delegate completion:^(STPPaymentHandlerActionStatus handlerStatus, STPPaymentIntent * handledIntent, NSError * _Nullable handlerError) { switch (handlerStatus) { case STPPaymentHandlerActionStatusFailed: // Payment failed // ... break; case STPPaymentHandlerActionStatusCanceled: // Payment canceled // ... break; case STPPaymentHandlerActionStatusSucceeded: // Payment succeeded // ... break; } }];

Handle post-payment events

Because Sofort is a delayed notification payment method, the PaymentIntent object’s status remains in a payment_intent.processing state for up to 14 days from its creation (also known as the cut-off date). In test mode, the PaymentIntent object’s status will remain in a payment_intent.processing state for three minutes to simulate this.

We recommend that you fulfill purchases made with Sofort when the payment intent enters the payment_intent.processing state. On average, you can expect approximately 0.2% of Sofort payment attempts to fail after entering the payment_intent.processing state. You may prefer to fulfill orders only after receiving the payment_intent.succeeded webhook. Note that this guidance only applies to Sofort payments due to its low payment failure rate and does not apply to other delayed notification payment methods.

After the payment attempt is confirmed—and the funds guaranteed—its status updates to payment_intent.succeeded.

If a customer doesn’t pay, the PaymentIntent emits the payment_intent.failed webhook event and returns to a status of requires_payment_method.

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.

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.

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.

View your test payments in the Dashboard

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.

Build a custom webhook

Prebuilt apps

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

Browse shipping apps and extensions

Browse inventory management apps and extensions

See all partner solutions

## Disputed payments {#ios-disputed-payments}

The risk of fraud or unrecognized payments is low because the customer must authenticate the payment with their bank. As a result, you won’t have disputes that turn into chargebacks, with funds withdrawn from your Stripe account.

Failed attempts

If a payment attempt hasn’t been confirmed within the cut-off time, the PaymentIntent object’s status automatically transitions from processing to requires_payment_method. Additionally, if the funds are received after the cut-off date, the customer is automatically refunded.

On average, you can expect approximately 0.2% of Sofort payment attempts to fail. This may vary based on your industry or customer base. Depending on your average payment amount, the type of products or service you provide, and the risk associated with your business, you may prefer to fulfill orders only after receiving the payment_intent.succeeded webhook.

Refunds

Sofort only accepts refund requests within 180 days from the date of the original payment. After 180 days, it’s no longer possible to refund the payment.

You can submit a refund against pending charges that haven’t been confirmed yet. If you create a full or partial refund on a processing PaymentIntent, the refund occurs only after the PaymentIntent’s status is transitioned to succeeded. If the PaymentIntent object transitions to status requires_payment_method after a payment attempt failure, full and partial refunds will be marked as canceled, as the money never left the customer’s bank account.

Accepting Sofort in your app consists of displaying a webview for a customer to authenticate their payment. It typically takes 2 to 14 days for you to receive notification of success or failure.

To accept Sofort, you must comply with our Sofort Terms of Service.

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:

Ruby Python PHP Java Node Go .NET
Terminal
# Available as a gem gem install stripe
# Available as a gem gem install stripe
Gemfile
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
Terminal
# Install through pip pip install --upgrade stripe
# Install through pip pip install --upgrade stripe
PyPI
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
requirements.txt
# 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
# 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
Terminal
# Install the PHP library via Composer composer require stripe/stripe-php
# Install the PHP library via Composer composer require stripe/stripe-php
Source
# Or download the source directly: https://github.com/stripe/stripe-php/releases
# Or download the source directly: https://github.com/stripe/stripe-php/releases
build.gradle
/* 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 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}"
pom.xml
<!-- 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 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>
Other environments
# 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
# 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
Terminal
# Install via npm npm install --save stripe
# Install via npm npm install --save stripe
Terminal
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
app.go
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
Terminal
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Install via dotnet dotnet add package Stripe.net dotnet restore
Terminal
# Or install via NuGet PM> Install-Package Stripe.net
# 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:

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

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.

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

Kotlin Java
import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext, "pk_test_TYooMQauvdEDq54NiTphI7jx" ) } }
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" ); } }
import com.stripe.android.PaymentConfiguration; public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); PaymentConfiguration.init( getApplicationContext(), "pk_test_TYooMQauvdEDq54NiTphI7jx" ); } }

Use your test mode keys while you test and develop, and your live mode keys before you publish your app.

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

2 Create a PaymentIntent Server-side

Server-side

A PaymentIntent is an object that represents your intent to collect payment 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. If you already have an integration using the Payment Intents API, add sofort to the list of payment method types for your PaymentIntent.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], })
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], })
# 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( amount=1099, currency='eur', payment_method_types=['sofort'] )
# 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( amount=1099, currency='eur', payment_method_types=['sofort'] )
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], ]);
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], ]);
// 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("sofort") .build(); 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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("eur") .addPaymentMethodType("sofort") .build(); 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], });
// 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{ "sofort", }), } 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyEUR)), PaymentMethodTypes: stripe.StringSlice([]string{ "sofort", }), } 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> { "sofort", }, }; var service = new PaymentIntentService(); var intent = service.Create(options);
// 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> { "sofort", }, }; var service = new PaymentIntentService(); var intent = service.Create(options);

Changing the preferred language

By default, Stripe presents the Sofort authorization page in a language based on the specified country code. You can customize this to the language preferred by your customer by specifying it as part of the request and changing the value of the preferred_language property. The supported values are de, en, es, it, fr, nl, and pl.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort \ -d "payment_method_options[sofort][preferred_language]"=de
curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofort \ -d "payment_method_options[sofort][preferred_language]"=de
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, })∂
# 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({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, })∂
# 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( amount=1099, currency='eur', payment_method_types=['sofort'], payment_method_options={ 'sofort': { 'preferred_language': 'de', }, }, )
# 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( amount=1099, currency='eur', payment_method_types=['sofort'], payment_method_options={ 'sofort': { 'preferred_language': 'de', }, }, )
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], 'payment_method_options' => [ 'sofort' => [ 'preferred_language' => 'de', ], ], ]);
// 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([ 'amount' => 1099, 'currency' => 'eur', 'payment_method_types' => ['sofort'], 'payment_method_options' => [ 'sofort' => [ 'preferred_language' => 'de', ], ], ]);
// 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("sofort") .setPaymentMethodOptions( PaymentIntentCreateParams.PaymentMethodOptions.builder() .putExtraParam("sofort[preferred_language]", "de") .build()) .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("eur") .addPaymentMethodType("sofort") .setPaymentMethodOptions( PaymentIntentCreateParams.PaymentMethodOptions.builder() .putExtraParam("sofort[preferred_language]", "de") .build()) .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'eur', payment_method_types: ['sofort'], payment_method_options: { sofort: { preferred_language: 'de', }, }, });
// 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{ "sofort", }), } params.AddExtra("payment_method_options[sofort][preferred_language]", "de") 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyEUR)), PaymentMethodTypes: stripe.StringSlice([]string{ "sofort", }), } params.AddExtra("payment_method_options[sofort][preferred_language]", "de") 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> { "sofort", }, }; options.AddExtraParam("payment_method_options[sofort][preferred_language]", "de"); var service = new PaymentIntentService(); var intent = service.Create(options);
// 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> { "sofort", }, }; options.AddExtraParam("payment_method_options[sofort][preferred_language]", "de"); var service = new PaymentIntentService(); var intent = service.Create(options);

Client-side

On the client, request a PaymentIntent from your server and store its client secret.

Kotlin Java
class SofortPaymentActivity: AppCompatActivity() { private lateinit var paymentIntentClientSecret: String override fun onCreate(savedInstanceState: Bundle?) { // ... startCheckout() } private fun startCheckout() { // Request a PaymentIntent from your server and store its client secret } }
class SofortPaymentActivity: AppCompatActivity() { private lateinit var paymentIntentClientSecret: String override fun onCreate(savedInstanceState: Bundle?) { // ... startCheckout() } private fun startCheckout() { // Request a PaymentIntent from your server and store its client secret } }
CheckoutViewController.m
public class SofortPaymentActivity extends AppCompatActivity { private String paymentIntentClientSecret; @Override public void onCreate(@Nullable Bundle savedInstanceState) { // ... startCheckout(); } private void startCheckout() { // Request a PaymentIntent from your server and store its client secret } }
public class SofortPaymentActivity extends AppCompatActivity { private String paymentIntentClientSecret; @Override public void onCreate(@Nullable Bundle savedInstanceState) { // ... startCheckout(); } private void startCheckout() { // Request a PaymentIntent from your server and store its client secret } }

3 Collect payment method details Client-side

In your app, collect country code of the user’s bank. You can then create a PaymentMethodCreateParams with this information.

Kotlin Java
val paymentMethodCreateParams = PaymentMethodCreateParams.create( PaymentMethodCreateParams.Sofort(country = "de") )
val paymentMethodCreateParams = PaymentMethodCreateParams.create( PaymentMethodCreateParams.Sofort(country = "de") )
PaymentMethodCreateParams paymentMethodCreateParams = PaymentMethodCreateParams.create( // Pass the collected country code to the Sofort constructor new PaymentMethodCreateParams.Sofort("de") );
PaymentMethodCreateParams paymentMethodCreateParams = PaymentMethodCreateParams.create( // Pass the collected country code to the Sofort constructor new PaymentMethodCreateParams.Sofort("de") );

4 Submit the payment to Stripe Client-side

Retrieve the client secret from the PaymentIntent you created in step 2 and call Stripe#confirmPayment(). This presents a webview where the customer can authorize the payment. Afterwards, onActivityResult is called with the result of the payment.

Kotlin Java
class SofortPaymentActivity : 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.Processing -> { // Payment authorized } else -> { // Payment failed/cancelled } } } override fun onError(e: Exception) { // Payment failed } }) } }
class SofortPaymentActivity : 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.Processing -> { // Payment authorized } else -> { // Payment failed/cancelled } } } override fun onError(e: Exception) { // Payment failed } }) } }
public class SofortPaymentActivity 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(); PaymentIntent.Status status = paymentIntent.getStatus(); if (status == StripeIntent.Status.Processing) { // Payment authorized } else { // Payment failed/cancelled } } @Override public void onError(@NonNull Exception e) { // Payment failed } } }
public class SofortPaymentActivity 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(); PaymentIntent.Status status = paymentIntent.getStatus(); if (status == StripeIntent.Status.Processing) { // Payment authorized } else { // Payment failed/cancelled } } @Override public void onError(@NonNull Exception e) { // Payment failed } } }

Handle post-payment events

Because Sofort is a delayed notification payment method, the PaymentIntent object’s status remains in a payment_intent.processing state for up to 14 days from its creation (also known as the cut-off date). In test mode, the PaymentIntent object’s status will remain in a payment_intent.processing state for three minutes to simulate this.

We recommend that you fulfill purchases made with Sofort when the payment intent enters the payment_intent.processing state. On average, you can expect approximately 0.2% of Sofort payment attempts to fail after entering the payment_intent.processing state. You may prefer to fulfill orders only after receiving the payment_intent.succeeded webhook. Note that this guidance only applies to Sofort payments due to its low payment failure rate and does not apply to other delayed notification payment methods.

After the payment attempt is confirmed—and the funds guaranteed—its status updates to payment_intent.succeeded.

If a customer doesn’t pay, the PaymentIntent emits the payment_intent.failed webhook event and returns to a status of requires_payment_method.

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.

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.

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.

View your test payments in the Dashboard

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.

Build a custom webhook

Prebuilt apps

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

Browse shipping apps and extensions

Browse inventory management apps and extensions

See all partner solutions

## Disputed payments {#android-disputed-payments}

The risk of fraud or unrecognized payments is low because the customer must authenticate the payment with their bank. As a result, you won’t have disputes that turn into chargebacks, with funds withdrawn from your Stripe account.

Failed attempts

If a payment attempt hasn’t been confirmed within the cut-off time, the PaymentIntent object’s status automatically transitions from processing to requires_payment_method. Additionally, if the funds are received after the cut-off date, the customer is automatically refunded.

On average, you can expect approximately 0.2% of Sofort payment attempts to fail. This may vary based on your industry or customer base. Depending on your average payment amount, the type of products or service you provide, and the risk associated with your business, you may prefer to fulfill orders only after receiving the payment_intent.succeeded webhook.

Refunds

Sofort only accepts refund requests within 180 days from the date of the original payment. After 180 days, it’s no longer possible to refund the payment.

You can submit a refund against pending charges that haven’t been confirmed yet. If you create a full or partial refund on a processing PaymentIntent, the refund occurs only after the PaymentIntent’s status is transitioned to succeeded. If the PaymentIntent object transitions to status requires_payment_method after a payment attempt failure, full and partial refunds will be marked as canceled, as the money never left the customer’s bank account.

Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.
You can unsubscribe at any time. Read our privacy policy.
On this page
Set up Stripe
Create a PaymentIntent
Submit the payment to Stripe
Handle post-payment events
Handle the Sofort redirect manually
Disputed payments
Failed attempts
Refunds