# Stripe verwenden, um Zahlungen mit SCA zu akzeptieren

> Formspree Docs · Plugins · 28. Februar 2026

#### Verfügbar in den Tarifen: Free (Test-Modus), Professional, Business

Stripe ist eine Zahlungsabwicklungsplattform, die es Händlern ermöglicht, Kreditkartenzahlungen in ihren Apps und auf ihren Websites zu akzeptieren. Wenn du dein Formular mit einem Stripe-Konto verbindest, kannst du Belastungen vornehmen und Zahlungen von deiner Website erhalten.  
  
**Was ist neu?**  
Das Stripe-Plugin (V2) unterstützt jetzt die serverseitige Validierung von Zahlungen, variable Belastungen für Spenden und optional [Strong Customer Authentication](https://stripe.com/docs/strong-customer-authentication).  
  
Strong Customer Authentication (SCA) ist eine Verordnung, die am 14. September 2019 in Kraft getreten ist. Sie erfordert Änderungen daran, wie europäische Kunden Online-Zahlungen authentifizieren. Diese Verordnung [gilt](https://stripe.com/docs/strong-customer-authentication#impact) nur für Online-Zahlungen, bei denen sich sowohl die Bank des Kunden als auch das Unternehmen im [Europäischen Wirtschaftsraum](https://en.wikipedia.org/wiki/European_Economic_Area) (EWR) befinden.  
  
Du erkennst, dass du das neue Stripe-Plugin V2 verwendest, wenn deine Plugin-Einstellungen einen Stripe Publishable Key anzeigen. Wenn du den Publishable Key nicht siehst, kannst du dein Plugin auf die neueste Version aktualisieren, indem du das Plugin löschst und neu verbindest. Es gehen keine Daten verloren.

## Schritt 1: Verbindung mit Stripe herstellen

### Test-Modus vs. Live-Modus

 Der Test- und der Live-Modus funktionieren nahezu identisch, mit ein paar notwendigen Unterschieden:

-   Im Test-Modus werden Zahlungen nicht von Kartennetzwerken oder Zahlungsanbietern verarbeitet, und es können nur unsere [Test-Zahlungsinformationen](https://stripe.com/docs/testing) verwendet werden.
-   Einige Zahlungsmethoden, die [Sources](https://stripe.com/docs/sources) verwenden, haben im Live-Modus einen differenzierteren Ablauf mit mehr erforderlichen Schritten als im Test-Modus.
-   Auch Streitfälle haben im Live-Modus einen differenzierteren Ablauf und einen einfacheren [Testprozess](https://stripe.com/docs/testing#disputes).
-   Im Test-Modus werden [Webhooks](https://stripe.com/docs/webhooks), die nicht erfolgreich bestätigt wurden, über einige Stunden hinweg dreimal wiederholt (im Gegensatz zu 72 Stunden im Live-Modus).

Um Test-Belastungen zu sehen, gehe zu deinem Stripe-Dashboard und klicke auf „View Test Data".

  
![mceclip0.png](/images/zendesk/9479210aa8c1e005.png)

_**Beachte: Wenn du vom Test-Modus in den Live-Modus wechseln möchtest, musst du das Plugin löschen und erneut erstellen, um über Stripe OAuth neue gültige Anmeldedaten zu erhalten.**_

_Wir empfehlen, ein Formular für die Entwicklungs-/Staging-Umgebung mit dem Test-Modus und ein Produktionsformular mit dem Live-Modus zu verbinden._

### Das Stripe-Plugin einrichten

Sobald du in Formspree ein Formular hinzugefügt hast, kannst du dich über das Stripe-Plugin mit Stripe verbinden. Gehe dazu zuerst zum Tab „Plugins" deines Formulars. Klicke dann auf die Schaltfläche des Stripe-Plugins.

![mceclip0.png](/images/zendesk/e61d8f0b2e0b9e6c.png)

Nachdem du auf die Schaltfläche des Stripe-Plugins geklickt hast, kannst du dich im Test- oder Live-Modus verbinden.  
Der **Test-Modus** ist in allen Tarifen verfügbar. Der **Live-Modus** ist nur in aufgewerteten Tarifen verfügbar.

![mceclip4.png](/images/zendesk/ac89283388ab406b.png)

Wähle den gewünschten Modus aus und klicke auf „connect". Sobald du dich mit deinem Stripe-Benutzernamen und -Passwort angemeldet hast, wirst du aufgefordert, die Stripe-Belastungsinformationen für dieses Formular festzulegen. Lege die Währung und den Preis fest, der jedes Mal belastet wird, wenn das Formular abgesendet wird.  
  
Wenn du auf **allow variable price** klickst, kann der Entwickler oder Website-Besucher den Preis clientseitig festlegen. So kann ein Formular einen variablen Betrag belasten, etwa für eine Spende. Es kann auch verwendet werden, damit ein Formular je nach anderen Faktoren, wie einem ausgewählten Produkt, unterschiedliche Preise belastet. Sobald **allow variable price** angeklickt ist, wird der Preis aus dem Formularfeld **price** gelesen.   
  
_**Vorsicht: Wenn variable Preisgestaltung aktiviert ist, kann jeder Kunde diesen Wert direkt oder durch Reverse-Engineering deines Codes ändern und Belastungen in beliebiger Höhe erzeugen. Achte darauf, die an dein Stripe-Konto gesendeten Belastungen zu überprüfen. Wir validieren diesen Wert nicht.**_

![mceclip0.png](/images/zendesk/f97e2f49dac06df8.png)

Sobald du die Einrichtung abgeschlossen hast, klicke auf **Connect**, um das Plugin zu aktivieren.

Wenn du in Zukunft die Beschreibung und den Preis ändern oder das Plugin vollständig deaktivieren möchtest, kannst du das tun, indem du erneut auf die Schaltfläche „Stripe" klickst, um die Einstellungen anzupassen.

Sobald du dein Stripe-Plugin eingerichtet und verbunden hast, siehst du diese Meldung:

![mceclip1.png](/images/zendesk/e07fed14e1716088.png)

Kopiere den **Publishable Key**. Wir verwenden ihn, um unseren Checkout-Workflow aufzubauen.

## Schritt 2: Erstelle deinen Checkout-Ablauf mit Stripe Elements (kein SCA erforderlich)

**HINWEIS**: Wenn du React verwendest, sieh dir unser Tutorial an: [So baust du ein benutzerdefiniertes React-Zahlungsformular mit Stripe](https://formspree.io/blog/custom-payment-stripe/).

Jetzt, da du dein Formspree-Formular mit dem Stripe-Plugin eingerichtet hast, ist es an der Zeit, deinen Checkout-Ablauf aufzubauen. Um Zahlungen zu belasten, musst du eine Kreditkarte tokenisieren, um sie sicher an Formspree zu übertragen.

Ein einfacher Weg dazu ist die Verwendung von [Stripe Elements](https://stripe.com/payments/elements). Das ist eine Reihe vorgefertigter UI-Komponenten, wie Eingabefelder und Schaltflächen, zum Aufbau deines Checkout-Ablaufs. Es ist als Funktion von Stripe.js verfügbar. Stripe.js _tokenisiert_ die sensiblen Informationen innerhalb eines Elements, ohne dass sie jemals deine oder unsere Server berühren.  
  
Dann musst du nur die tokenisierte Zahlungsmethode an Formspree senden, das die Zahlung erstellt und belastet.

Hier ist ein Beispiel, inspiriert von den [Stripe-Docs](https://stripe.com/docs/payments/accept-a-payment-synchronously):

**HTML-Datei**

```html
<head>
  <title>Checkout</title>
  <script src="https://js.stripe.com/v3/"></script>
  <script src="https://code.jquery.com/jquery-latest.min.js"></script>
 </head>

 <body>
   <form id="payment-form" action="">
     <div id="card-element">
       <!-- Elements will create input elements here -->
     </div>

     <!-- We'll put the error messages in this element -->
     <div id="card-errors" role="alert"></div>

     <button id="submit">Pay</button>
   </form>
 </body>
```

**JS-Datei**

```javascript
$(document).ready(function () {
  // Stripe settings
  var stripe = Stripe("YOUR_PUBLISHABLE_KEY_COPIED_FROM_DASHBOARD");
  var elements = stripe.elements();
  var style = {
    base: {
      color: "#32325d",
    },
  };
  var card = elements.create("card", {
    style: style,
  });

  card.mount("#card-element");
  card.on("change", ({ error }) => {
    const displayError = document.getElementById("card-errors");
    if (error) {
      displayError.textContent = error.message;
    } else {
      displayError.textContent = "";
    }
  });
  // end stripe

  var form = document.getElementById("payment-form");
  form.addEventListener("submit", function (event) {
    event.preventDefault();
    // Tokenize card info
    stripe
      .createPaymentMethod({
        type: "card",
        card: card,
        billing_details: {
          // Include any additional collected billing details.
          name: "Jenny Rosen",
          email: "jenny@rosen.com",
        },
      })
      .then(stripePaymentMethodHandler);
  });

  function stripePaymentMethodHandler(result) {
    if (result.error) {
      // Show error to user
      console.log(result.error);
    } else {
      // Otherwise send payment method to your server
      fetch("https://formspree.io/f/FORM_ID", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          // Send the tokenized card
          paymentMethod: result.paymentMethod.id,
          email: "jenny@rosen.com",
          // add here any data that you want to send to Formspree
        }),
      }).then(function (result) {
        // Handle server response
        result.json().then(function (json) {
          handleServerResponse(json);
        });
      });
    }
  }

  function handleServerResponse(response) {
    if (response.error) {
      // Display errors on the payment form
      console.log(response.error);
    } else {
      // Display a success message
      console.log("Payment finished successfully");
    }
  }
});
```

Um deine Stripe Elements zu gestalten, sieh dir [dieses Beispiel](https://gist.github.com/colevscode/9cf19623e157cbe6822ffb8bd6e707fb) an oder unsere Beispiel-Reset-Styles für Formulare [hier](https://gist.github.com/colevscode/ce67075b62f843b7e95b1d5a1370035b).

An diesem Punkt hast du ein funktionierendes Formular, das Belastungen für Karteninhaber außerhalb der EU vornehmen kann.

## Schritt 3: (Optional) SCA-Zahlungen unterstützen

Nach Abschluss von Schritt 2 sollte dein Formular korrekt absenden und Belastungen in Stripe erstellen. Wenn jedoch die Bank des Absenders SCA erfordert, erzeugt das Formular einen Fehler. Um SCA zu unterstützen, müssen wir dem vorherigen Ablauf einen weiteren Schritt hinzufügen.

Bei der ersten Anfrage versucht der Formspree-Server, einen [Payment Intent](https://stripe.com/docs/payments/payment-intents) zu erstellen und zu belasten. Wenn die Karte eine zusätzliche Aktion wie SCA erfordert, schlägt die Belastung fehl, und der Payment Intent gibt den Status `requires_action` zurück.

Der Formspree-Server gibt einen HTTP-Status 402 mit den folgenden Informationen zurück:

-   Die erstellte Payment-Intent-ID
-   Ein Payment-Intent-Client-Secret, das verwendet wird, um eine Anfrage an Stripe zu stellen, um die Zahlung zu validieren
-   Einen `resubmit_key`, der verwendet wird, um die Anfrage erneut an Formspree zu senden, sobald die Zahlung validiert wurde

#### **Beispiel für einen Response-Body**

```javascript
{
  "message": "requires_action",
  "resubmitKey": <key_generated_by_formspree>,
  "stripe": {
    "paymentIntentId": <id_from_the_payment_intent>,
    "paymentIntentClientSecret": <secret_generated_by_stripe>,
    "requiresAction": true
  }
}
```

Wir können die Zahlung nun validieren, indem wir Stripes Funktion [handleCardAction](https://stripe.com/docs/js/payment_intents/handle_card_action) aufrufen und das `payment_intent_client_secret` senden. Dadurch wird jede erforderliche Frontend-Validierung durchgeführt, etwa das Öffnen des [3DS-Modals](https://stripe.com/docs/payments/3d-secure), um SCA abzuschließen.

Das folgende Beispiel zeigt, wie die Funktion `handleServerResponse()`, die wir im letzten Schritt erstellt haben, aktualisiert wird, um erforderliche Aktionen abzufangen. Anschließend wird das Formular mit dem Payment Intent und einem neuen `resubmit_key` erneut abgesendet.  
  
**JS-Beispiel**

```javascript
function handleServerResponse(response) {
  if (response.error) {
    // Show error from server on payment form
    console.log(response.error);
  } else if (response && response.stripe.requiresAction) {
    // Stripe require additional actions to charge this card
    // Use Stripe.js to handle required card action and open 3DS
    stripe
      .handleCardAction(response.stripe.paymentIntentClientSecret)
      .then(function (stripeResult) {
        // TODO: implement this function to resubmit the form
        resubmitForm(stripeResult, response.resubmitKey);
      });
  } else {
    console.log("Payment finished successfully");
  }
}
```

#### **Stripe-3DS-Modal**

![mceclip2.png](/images/zendesk/dcb1c309939d07c1.png)

Wenn die Funktion [handleCardAction](https://stripe.com/docs/js/payment_intents/handle_card_action) erfolgreich ist, kannst du das Formular nun erneut an Formspree senden und den `resubmit_key` sowie den `paymentIntent` senden, den du zuvor nach dem ersten fehlgeschlagenen Versuch erhalten hast.

**Beispiel für das erneute Absenden des Formulars:**

```javascript
function resubmitForm(result, resubmitKey) {
  console.log("handle Stripe SCA result");
  if (result.error) {
    // Show error in payment form
    console.log("Stripe SCA error");
  } else {
    // The card action has been handled
    // The PaymentIntent can be confirmed again on the server
    fetch('https://formspree.io/f/', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ 
        paymentIntent: result.paymentIntent.id ,
        resubmitKey: resubmitKey,
        email: 'jenny@rosen.com',
      })
    }).then(function(result) {
      return result.json();
    }).then(handleServerResponse);
  }
}
```

Wenn Formspree die zweite Übermittlung erhält, versucht es erneut, den Payment Intent mit den Karteninformationen zu belasten, die clientseitig über Stripes SCA-Ablauf validiert worden sein sollten. Du kannst dieselben Felder aus der ursprünglichen Übermittlung erneut senden. Alle Änderungen an diesen Feldern aktualisieren die Formulardaten.

Beachte, dass der `resubmit_key` abläuft, wenn er nicht zeitnah verwendet wird. In diesem Fall gibt Formspree einen HTTP-404-Response-Statuscode zurück.

## Zusammenfassung

Zusammengefasst sind die Schritte folgende:

1.  Versuche, die Karte zu belasten. Wenn SCA erforderlich ist, wird eine 402-Antwort mit den Payment-Intent-Daten zurückgegeben und der Schlüssel erneut gesendet.
2.  Rufe Stripes handleCardAction mit dem Payment-Intent-Client-Secret auf, das beim ersten Versuch zurückgegeben wurde, und führe [3DS](https://stripe.com/docs/payments/3d-secure) durch.
3.  Sende das Formular erneut, indem du den Resubmit-Key, die Payment-Intent-ID und die E-Mail sendest. Formspree versucht erneut zu belasten, um die Übermittlung abzuschließen.

Sieh dir den vollständigen Checkout-Ablauf mit SCA an  
  
**JS-Datei (SCA)**

```javascript
$(document).ready(function () {
  // Stripe settings
  var stripe = Stripe("YOUR_PUBLISHABLE_KEY_COPIED_FROM_DASHBOARD");
  var elements = stripe.elements();
  var style = {
    base: {
      color: "#32325d",
    },
  };
  var card = elements.create("card", {
    style: style,
  });

  card.mount("#card-element");
  card.on("change", ({ error }) => {
    const displayError = document.getElementById("card-errors");
    if (error) {
      displayError.textContent = error.message;
    } else {
      displayError.textContent = "";
    }
  });
  // end stripe

  var form = document.getElementById("payment-form");
  form.addEventListener("submit", function (ev) {
    event.preventDefault();

    stripe
      .createPaymentMethod({
        type: "card",
        card: card,
        billing_details: {
          // Include any additional collected billing details.
          name: "Jenny Rosen",
          email: "jenny@rosen.com",
        },
      })
      .then(stripePaymentMethodHandler);
  });

  function stripePaymentMethodHandler(result) {
    if (result.error) {
      console.log("Error creating payment method");
    } else {
      // First submission attempt
      fetch("https://formspree.io/f/FORM_ID", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          paymentMethod: result.paymentMethod.id,
          email: "jenny@rosen.com",
        }),
      }).then(function (response) {
        // Handle server response (see Step 4)
        response.json().then(function (json) {
          handleServerResponse(json);
        });
      });
    }
  }

  function handleServerResponse(response) {
    if (response.stripe && response.stripe.requiresAction) {
      // Stripe require additional actions to charge this card
      // Use Stripe.js to handle required card action and open 3DS
      stripe
        .handleCardAction(response.stripe.paymentIntentClientSecret)
        .then(function (stripeResult) {
          // Get handleCardAction response and resubmit
          resubmitForm(stripeResult, response.resubmitKey);
        });
    } else if (response.error) {
      // Show error from server on payment form
      console.log(response.error);
    } else {
      console.log("Payment finished successfully");
    }
  }

  function resubmitForm(result, resubmitKey) {
    console.log("handle Stripe SCA result");
    if (result.error) {
      // Display error in the payment form
      console.log("Stripe SCA error");
    } else {
      // The card action has been handled
      // The PaymentIntent can be confirmed again on the server
      fetch("https://formspree.io/f/FORM_ID", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          paymentIntent: result.paymentIntent.id,
          resubmitKey: resubmitKey,
          email: "jenny@rosen.com",
        }),
      })
        .then(function (confirmResult) {
          return confirmResult.json();
        })
        .then(handleServerResponse);
    }
  }
});
```

Um mehr über das Testen mit Stripe und Stripe SCA zu erfahren, rufe [https://stripe.com/docs/testing](https://stripe.com/docs/testing) und unseren [Blogbeitrag](https://formspree.io/blog/custom-payment-stripe/) auf.
