PDA-LOG-003
FINTECH

Stripe Custom Flows vs. Out-of-the-Box

Stripe Checkout löst 80% der Anwendungsfälle. Die übrigen 20% — komplexe Subscriptions, Multi-Party-Payments, Custom Billing — erfordern eine eigene Payment-Infrastruktur.

Publiziert05.01.2026
Lesezeit06:00 MIN
AutorPalmer Digital

Stripe ist die beste Payment-Infrastruktur, die es gibt. Das ist keine Meinung, das ist ein Marktbefund. Aber Stripe ist auch ein Werkzeug mit Schichten — und die meisten Entwickler nutzen nur die oberste Schicht. Wer das volle Potential ausschöpfen will, muss verstehen, wann Stripe Checkout ausreicht und wann man tiefer in die API einsteigen muss.

Die drei Integrationsebenen

Stripe bietet im Wesentlichen drei Integrationstiefen: Stripe Checkout (hosted), Stripe Payment Element (embedded) und direkte PaymentIntent/SetupIntent-Integration. Jede Ebene bietet mehr Kontrolle, erfordert aber auch mehr Implementierungsaufwand und trägt mehr PCI-DSS-Verantwortung.

  • Stripe Checkout (Hosted): Maximale PCI-Absicherung, minimaler Code, aber keine Custom-UI und eingeschränkte Branding-Kontrolle
  • Stripe Payment Element (Embedded): Custom-UI mit Stripe-gehosteter Card-Tokenization — das beste Balance-Verhältnis für die meisten Anwendungsfälle
  • Direkte API-Integration: Vollständige Kontrolle, aber PCI SAQ D-Compliance erforderlich — nur für Spezialanforderungen empfohlen

PaymentIntent vs. SetupIntent: Die richtige Wahl

PaymentIntent und SetupIntent sind die Kernkonstrukte der Stripe API. PaymentIntent repräsentiert eine Zahlungsabsicht — Geld soll jetzt oder bald transferiert werden. SetupIntent speichert eine Zahlungsmethode für zukünftige Nutzung, ohne sofortigen Transfer. Die Verwechslung dieser beiden führt zu häufigen Implementierungsfehlern.

SetupIntent für Subscription mit Trial Period
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

// SetupIntent: Zahlungsmethode speichern, kein sofortiger Charge
export async function setupSubscriptionPayment(customerId: string) {
    const setupIntent = await stripe.setupIntents.create({
        customer: customerId,
        payment_method_types: ["card", "sepa_debit"],
        usage: "off_session", // Erlaubt spätere automatische Charges
        metadata: {
            purpose: "subscription_setup",
            environment: process.env.NODE_ENV!,
        },
    });

    return { clientSecret: setupIntent.client_secret };
}

// Nach erfolgreichem Setup: Subscription erstellen
export async function createSubscription(
    customerId: string,
    paymentMethodId: string,
    priceId: string
) {
    const subscription = await stripe.subscriptions.create({
        customer: customerId,
        items: [{ price: priceId }],
        default_payment_method: paymentMethodId,
        trial_period_days: 14,
        payment_settings: {
            save_default_payment_method: "on_subscription",
        },
        expand: ["latest_invoice.payment_intent"],
    });

    return subscription;
}

Idempotency Keys: Die unsichtbare Sicherheitsschicht

Idempotency Keys sind das am häufigsten übersehene Feature der Stripe API — und gleichzeitig eines der kritischsten. Netzwerkfehler, Timeouts und Retry-Logik können ohne Idempotency Keys zu Doppelbelastungen führen. Stripe garantiert, dass derselbe Request mit demselben Idempotency Key nur einmal verarbeitet wird — eine fundamentale Sicherheitsgarantie für jede Payment-Infrastruktur.

Idempotency Keys für sichere Retry-Logik
import { createHash } from "crypto";

function generateIdempotencyKey(
    userId: string,
    amount: number,
    currency: string,
    timestamp: number
): string {
    // Deterministischer Key basierend auf Transaktionsparametern
    return createHash("sha256")
        .update(`${userId}-${amount}-${currency}-${timestamp}`)
        .digest("hex")
        .substring(0, 32);
}

export async function chargeCustomer(
    customerId: string,
    amount: number,
    currency: string = "eur"
) {
    const timestamp = Math.floor(Date.now() / 60000); // 1-Minute Window
    const idempotencyKey = generateIdempotencyKey(
        customerId,
        amount,
        currency,
        timestamp
    );

    return await stripe.paymentIntents.create(
        {
            amount,
            currency,
            customer: customerId,
            confirm: true,
            automatic_payment_methods: { enabled: true },
        },
        { idempotencyKey }
    );
}

Webhook-Infrastruktur: Die kritische Verbindung

Webhooks sind die Achillesferse vieler Stripe-Integrationen. Der Zahlungsstatus wird von Stripe asynchron kommuniziert — das bedeutet, die Applikation muss Webhooks zuverlässig empfangen, verifizieren und verarbeiten. Ein ausgefallener Webhook-Endpoint bedeutet verlorene Zahlungsbestätigungen, nicht erfüllte Orders und Kundenbeschwerden.

ACHTUNG

Verarbeiten Sie Webhooks niemals synchron im HTTP-Handler. Empfangen, Signatur verifizieren, in Queue schreiben, 200 antworten — das ist der einzig korrekte Pattern. Alles andere führt zu Timeouts und verlorenen Events bei Lastspitzen.

TIP

Stripe's Webhook-Signatur-Verifizierung (stripe.webhooks.constructEvent) schützt vor gefälschten Events. Speichern Sie den Webhook-Signing-Secret ausschließlich als Environment Variable, nie im Code-Repository.