Sofort payments
Learn how to accept Sofort, a common payment method in Europe.
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.
1 Set up Stripe Server-side
First, you need a Stripe account. Register now.
Use our official libraries for access to the Stripe API from your application:
# Available as a gem gem install stripe# Available as a gem gem install stripe
# 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'
# Install through pip pip install --upgrade stripe# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency><!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore# Install via dotnet dotnet add package Stripe.net dotnet restore
# 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 https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofortcurl 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 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]"=decurl 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.
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.
<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.
// 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');
// 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.
// 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', } );
// 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', } );
Install React Stripe.js and the Stripe.js loader from the npm public registry.
npm install --save @stripe/react-stripe-js @stripe/stripe-jsnpm 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>
Add Stripe.js and Elements to your page
To use Element components, wrap the root of your React app in an Elements provider. Call loadStripe
with your publishable key and pass the returned Promise
to the Elements
provider.
import React from 'react'; import ReactDOM from 'react-dom'; import {Elements} from '@stripe/react-stripe-js'; import {loadStripe} from '@stripe/stripe-js'; import CheckoutForm from './CheckoutForm'; // Make sure to call `loadStripe` outside of a component’s render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe("pk_test_TYooMQauvdEDq54NiTphI7jx"); function App() { return ( <Elements stripe={stripePromise}> <CheckoutForm /> </Elements> ); }; ReactDOM.render(<App />, document.getElementById('root'));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.
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> ); }
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
.
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.
Custom code
Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.
Prebuilt apps
Handle common business events, like shipping and inventory management, by integrating a partner application.
Browse shipping apps and extensions
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:
- Create and confirm a PaymentIntent of type
sofort
. You must provide thepayment_method_data.sofort.country
property as it’s required by Sofort. Note that by specifyingpayment_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 thereturn_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 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);
- Check that the
PaymentIntent
has a status ofrequires_action
and the type fornext_action
isredirect_to_url
.
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" ] }{ "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,
- Redirect the customer to the URL provided in the
next_action.redirect_to_url.url
property. The code example here is approximate—the redirect method may be different in your web framework.
if payment_intent.status == 'requires_action' && payment_intent.next_action.type == 'redirect_to_url' url = payment_intent.next_action.redirect_to_url.url redirect(url) endif 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.
1 Determine compatibility
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
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:
- Add
sofort
to the list ofpayment_method_types
. - Make sure all your
line_items
use theeur
currency.
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.
1 Set up Stripe Server-side Client-side
First, you need a Stripe account. Register now.
Server-side
This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:
# Available as a gem gem install stripe# Available as a gem gem install stripe
# 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'
# Install through pip pip install --upgrade stripe# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency><!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore# Install via dotnet dotnet add package Stripe.net dotnet restore
# 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.
- If you haven't already, install the latest version of CocoaPods.
- If you don't have an existing Podfile, run the following command to create one:
pod init
pod init
- Add this line to your
Podfile
:pod 'Stripe'pod 'Stripe'
- Run the following command:
pod install
pod install
- Don't forget to use the
.xcworkspace
file to open your project in Xcode, instead of the.xcodeproj
file, from here on out. - In the future, to update to the latest version of the SDK, just run:
pod update Stripe
pod update Stripe
- If you haven't already, install the latest version of Carthage.
- Add this line to your
Cartfile
:github "stripe/stripe-ios"github "stripe/stripe-ios"
- Follow the Carthage installation instructions.
- In the future, to update to the latest version of the SDK, run the following command:
carthage update stripe-ios --platform ios
carthage update stripe-ios --platform ios
- Head to our GitHub releases page and download and unzip Stripe.framework.zip.
- Drag Stripe.framework to the "Embedded Binaries" section of your Xcode project's "General" settings. Make sure to select "Copy items if needed".
- 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"
- In the future, to update to the latest version of our SDK, just repeat steps 1 and 2.
When your app starts, configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API.
import UIKit import Stripe @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { StripeAPI.defaultPublishableKey = "pk_test_TYooMQauvdEDq54NiTphI7jx" // do any other necessary launch configuration return true } }import UIKit import Stripe @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { StripeAPI.defaultPublishableKey = "pk_test_TYooMQauvdEDq54NiTphI7jx" // do any other necessary launch configuration return true } }
#import "AppDelegate.h" @import Stripe; @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [StripeAPI setDefaultPublishableKey:@"pk_test_TYooMQauvdEDq54NiTphI7jx"]; // do any other necessary launch configuration return YES; } @end#import "AppDelegate.h" @import Stripe; @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [StripeAPI setDefaultPublishableKey:@"pk_test_TYooMQauvdEDq54NiTphI7jx"]; // do any other necessary launch configuration return YES; } @end
2 Create 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 https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofortcurl 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 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]"=decurl 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.
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 } }
@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.
// 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.
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.
Custom code
Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.
Prebuilt apps
Handle common business events, like shipping and inventory management, by integrating a partner application.
Browse shipping apps and extensions
## 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.
1 Set up Stripe Server-side Client-side
First, you need a Stripe account. Register now.
Server-side
This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:
# Available as a gem gem install stripe# Available as a gem gem install stripe
# 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'
# Install through pip pip install --upgrade stripe# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency><!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore# Install via dotnet dotnet add package Stripe.net dotnet restore
# 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:
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' }
Configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API, such as in your Application
subclass:
import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext, "pk_test_TYooMQauvdEDq54NiTphI7jx" ) } }import com.stripe.android.PaymentConfiguration 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" ); } }
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 https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=eur \ -d "payment_method_types[]"=sofortcurl 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 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]"=decurl 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.
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 } }
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.
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.
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.
Custom code
Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.
Prebuilt apps
Handle common business events, like shipping and inventory management, by integrating a partner application.
Browse shipping apps and extensions
## 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.