Payments
Other payment methods
Sofort
Accept a payment

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
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

2 Create 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[]"=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\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 const stripe = require('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 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]"=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\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 const stripe = require('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 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>

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

We also provide a UMD build for sites that do not use npm or modules.

Include the Stripe.js script, which exports a global Stripe function, and the UMD build of React Stripe.js, which exports a global ReactStripe object. Always load the Stripe.js script directly from js.stripe.com to remain PCI compliant. Do not include the script in a bundle or host a copy of it yourself.

<!-- Stripe.js --> <script src="https://js.stripe.com/v3/"></script> <!-- React Stripe.js development build --> <script src="https://unpkg.com/@stripe/react-stripe-js@latest/dist/react-stripe.umd.js"></script> <!-- When you are ready to deploy your site to production, remove the above development script, and include the following production build. --> <script src="https://unpkg.com/@stripe/react-stripe-js@latest/dist/react-stripe.umd.min.js"></script>

Add Stripe.js and Elements to your page

To use Element components, wrap the root of your React app in an Elements provider. Call loadStripe with your publishable key and pass the returned Promise to the Elements provider.

import React from 'react'; import ReactDOM from 'react-dom'; import {Elements} from '@stripe/react-stripe-js'; import {loadStripe} from '@stripe/stripe-js'; import CheckoutForm from './CheckoutForm'; // Make sure to call `loadStripe` outside of a component’s render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe("pk_test_TYooMQauvdEDq54NiTphI7jx"); function App() { return ( <Elements stripe={stripePromise}> <CheckoutForm /> </Elements> ); }; ReactDOM.render(<App />, document.getElementById('root'));

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 {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 } });
(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" }

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.

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:

  1. Add sofort to the list of payment_method_types.
  2. Make sure all your line_items use the eur 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( 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);
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)
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
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

Client-side

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

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

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

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

2 Create 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[]"=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\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 const stripe = require('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 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]"=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\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 const stripe = require('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 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 } }
@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: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { let stripeHandled = Stripe.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 ([Any]?) -> Void) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb { if let url = userActivity.webpageURL { let stripeHandled = Stripe.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 = [Stripe 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 = [Stripe 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() } }
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.

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
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

Client-side

The Android SDK is open source and fully documented.

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

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

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

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

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

2 Create 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[]"=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\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 const stripe = require('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 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]"=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\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 const stripe = require('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 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 } }
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") )
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 the Stripe confirmPayment method. 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 } }) } }
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.

Disputed payments

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

Failed attempts

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

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

Refunds

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

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

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