Stripe-Webhooks: Zahlungen zuverlässig verarbeiten
Kurz: Stripe meldet Zahlungen, Abos und Erstattungen per Webhook an deinen Server; du prüfst die Signatur, antwortest schnell, verarbeitest idempotent und verlässt dich nicht auf die Rückkehr-Seite des Kunden.
Genauer: Nach einer Zahlung kehrt der Kunde zu deiner Seite zurück, doch der Browser kann geschlossen werden oder die Verbindung abbrechen. Verlässlich ist nur die Servermeldung (Webhook) von Stripe: ein HTTP-POST mit einem Event (checkout.session.completed, invoice.paid, customer.subscription.deleted …).
Im Detail
Ablauf
- Endpunkt anlegen (
/api/stripe/webhook) und in Stripe (Dashboard oder CLI) Events auswählen. - Signatur prüfen mit dem Webhook-Secret und dem rohen Request-Body:
const sig = req.headers.get("stripe-signature")!;
const body = await req.text(); // roh, nicht JSON-geparst!
const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);Ohne Prüfung könnte jeder gefälschte Zahlungen melden. 3. Event verarbeiten (Bestellung bezahlt markieren, Abo-Status setzen, Rechnung versenden). 4. Schnell mit 2xx antworten; lange Arbeit in eine Warteschlange/Hintergrundjob.
Zuverlässigkeit
- Wiederholungen: Stripe sendet erneut, wenn du nicht 2xx antwortest (mehrere Tage). Dein Code muss idempotent sein: Event-ID (
event.id) speichern und doppelte ignorieren; Statusübergänge prüfen. - Reihenfolge nicht garantiert: Events können vertauscht eintreffen; bei Bedarf das Objekt neu bei Stripe abrufen.
- Datenbank-Ausfall: dann 5xx antworten, damit Stripe wiederholt, statt Ereignisse zu verlieren. Bei serverlosen Datenbanken, die erst aufwachen, genügend Timeout einplanen.
- Testmodus/Livemodus haben getrennte Secrets und Endpunkte.
- Stripe CLI (
stripe listen --forward-to localhost:3000/api/stripe/webhook) zum lokalen Testen.
Sicherheit und Recht
Secret geheim halten, nur HTTPS, nur nötige Events. Betrag und Währung serverseitig prüfen, nicht aus dem Browser übernehmen. Personenbezogene Daten sparsam speichern (Stripe speichert Kartendaten, nicht du: PCI-Last niedrig mit Checkout/Elements). Rechtliches: Widerrufsbelehrung, Rechnungen, AGB, Preisangaben, Kündigungsbutton bei Abos, Minderjährige und Verträge (Zustimmung der Eltern). Dies ist keine Rechtsberatung.
Typische Fehler
- Body vorher als JSON geparst → Signaturprüfung schlägt fehl.
- Falsches Secret (Test vs. Live, mehrere Endpunkte).
- Keine Idempotenz: doppelte Gutschriften/Mails.
- Zu langsame Antwort → Wiederholungsflut.
- Nur auf Redirect vertrauen.
- Ereignisse nicht protokolliert: Mit einem Event-Log (
stripe_events) lassen sich Fehler später nachvollziehen. Überwache fehlgeschlagene Zustellungen im Dashboard.
Siehe auch: Stripe, Webhooks, HMAC, Idempotenz, Zahlungsdienste für Minderjährige