Von Snapshot-Ereignissen zu Thin-Ereignissen migrieren
Erfahren Sie, wie Sie zu Thin-Ereignissen migrieren, ohne die Produktion zu unterbrechen.
Private Vorschau
Thin-Ereignisse für API v1-Ressourcen sind in der privaten Vorschau verfügbar. Bisher unterstützten Thin-Ereignisse nur API v2-Ressourcen. Erfahren Sie mehr und fordern Sie Zugriff an.
Thin-Ereignisse bieten eine leichte, versionsstabile Alternative zu Snapshot-Ereignissen. Anstatt vollständige Ressourcenobjekte in Webhook-Payloads zu empfangen, erhalten Sie kompakte Benachrichtigungen und rufen die benötigten Details ab. Dadurch müssen Webhook-Handler beim Upgrade von API-Versionen nicht mehr aktualisiert werden.
Verwenden Sie diesen Leitfaden, um von Snapshot-Ereignissen zu Thin-Ereignissen zu migrieren, ohne die Produktion zu unterbrechen. Bei der Migration wird eine Dual-Destination-Strategie verwendet, bei der beide Handler während des Übergangs parallel ausgeführt werden. Eine vollständige Übersicht über Thin-Ereignisse, einschließlich der Vorteile und Anwendungsszenarien, finden Sie unter Thin-Ereignisse.
Thin-Versionen von Snapshot-Ereignissen
Um bei der Migration zu helfen, erstellt Stripe Thin-Ereignisversionen Ihrer vorhandenen Snapshot-Ereignisse. Beispielsweise hat Kundin/Kundin. eine Thin-Version v1.. Wenn während der Migration eine einzelne Aktion beide Ereignisse auslöst, enthält die Thin-Version ein Feld snapshot_, das die ursprüngliche Snapshot-Ereignis-ID enthält. Verwenden Sie diesen als Idempotenz-Schlüssel, um eine doppelte Verarbeitung zu verhindern, wenn beide Handler gleichzeitig ausgeführt werden.
Bevor Sie loslegen
Stellen Sie sicher, dass Sie über Folgendes verfügen:
- Zugriff auf Ihren Webhook-Endpoint-Code, um eine neue Weiterleitung hinzuzufügen
- Berechtigung zum Erstellen von Ereigniszielen in Ihrem Stripe-Dashboard oder mit der API
- Zugriff auf eine Sandbox oder einen Test-Modus, um die Migration zu validieren, bevor die Produktion berührt wird
- Überwachung und Protokollierung zum Vergleich des Verhaltens zwischen Handlern
Warnung
Schließen Sie diesen gesamten Prozess in einer Sandbox oder im Test-Modus ab, bevor Sie ihn im Live-Modus versuchen.
Stufenweise Migrationsstrategie
Die Migration besteht aus folgenden Phasen:
- Fügen Sie Ihrer Anwendung einen neuen Thin-Webhook hinzu, der Sie weiterleitet.
- Erstellen Sie ein dünnes Ereignisziel, das Ihre bestehenden Abonnements widerspiegelt.
- Im Schattenmodus ausführen, um das Verhalten zu validieren, ohne Änderungen vorzunehmen.
- Führen Sie ein Cutover mit Idempotenz aus, um beide Ziele gleichzeitig zu verarbeiten.
- Setzen Sie das Snapshot-Ziel zurück, nachdem Sie die Stabilität bestätigt haben.
Diese Strategie stellt sicher, dass Sie keine Ausfallzeiten haben, und bietet Ihnen mehrere Möglichkeiten, sie bei Bedarf zu validieren und rückgängig zu machen.
Thin-Webhook-Route weiterleiten
Erstellen Sie einen neuen Endpoint in Ihrer Anwendung speziell für Thin-Ereignisse. Beginnen Sie mit einer minimalen Implementierung, die nur Signaturen verifiziert und einen 200-Statuscode zurückgibt.
Hinweis
Um auf das Feld snapshot_ zuzugreifen, konfigurieren Sie Ihr Stripe-SDK so, dass eine Vorschau-API verwendet wird:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY, { apiVersion: '2025-11-17.preview' });
Stabile API-Versionen (.) enthalten keine Funktionen für die private Vorschau wie snapshot_.
const express = require('express'); const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY, { apiVersion: '2025-11-17.preview' }); const app = express(); // New thin event endpoint app.post( '/webhook/thin', express.raw({type: 'application/json'}), async (req, res) => { const sig = req.headers['stripe-signature']; const thinWebhookSecret = process.env.THIN_WEBHOOK_SECRET; try { // Verify the signature using the same method as snapshot events const thinNotification = stripe.webhooks.constructEvent( req.body, sig, thinWebhookSecret ); console.log(`Verified thin event: ${thinNotification.id}`); // For now, just acknowledge receipt res.sendStatus(200); } catch (err) { console.log(`Webhook Error: ${err.message}`); res.status(400).send(`Webhook Error: ${err.message}`); } } ); app.listen(3000, () => console.log('Running on port 3000'));
Stellen Sie diese Änderung bereit und überprüfen Sie, ob der Endpoint zugänglich ist.
Dünnes Ereignisziel erstellen
Erstellen Sie im Stripe-Dashboard oder in der API ein neues Ereignisziel, das für Thin-Ereignisse konfiguriert ist.
Verwendung des Dashboards
- Gehen Sie zu Entwickler > Webhooks.
- Klicken Sie auf Ziel hinzufügen.
- Aktivieren Sie unter Erweiterte Einstellungen die Option Thin-Ereignisse verwenden.
- Abonnieren Sie die gleichen Ereignistypen, die Ihr Snapshot-Ziel verwendet, jedoch mit dem Präfix
v1.:customer.→created v1.customer. created payment_→intent. succeeded v1.payment_ intent. succeeded invoice.→paid v1.invoice. paid
- Geben Sie Ihre neue Endpoint-URL ein (zum Beispiel
https://yourdomain.)com/webhook/thin
Die API verwenden
curl https://api.stripe.com/v2/core/event_destinations \ -H "Authorization: Bearer sk_test_..." \ -H "Stripe-Version: 2025-11-17.preview" \ -d "name"="Thin Events Destination" \ -d "type"="webhook_endpoint" \ -d "event_payload"="thin" \ -d "webhook_endpoint[url]"="https://yourdomain.com/webhook/thin" \ -d "enabled_events[]"="v1.customer.created" \ -d "enabled_events[]"="v1.payment_intent.succeeded" \ -d "enabled_events[]"="v1.invoice.paid"
Hinweis
Speichern Sie das neue Webhook-Anmelde-Geheimnis getrennt von Ihrem Snapshot-Webhook-Geheimnis. Beschriften Sie es eindeutig (zum Beispiel SNAPSHOT_ und THIN_), um eine Verwechslung zu vermeiden.
Zu diesem Zeitpunkt sind beide Ziele aktiv und liefern Ereignisse an Ihre Anwendung.
Im Schattenmodus ausführen
Aktualisieren Sie Ihren Thin-Webhook-Handler, um Ereignisdetails und zugehörige Objekte abzurufen – schreiben Sie aber noch nicht in Ihre Datenbank. Protokollieren Sie stattdessen, welche Aktionen Sie ausführen würden, damit Sie überwachen können, ob sich Ihr Thin-Handler genauso verhält wie Ihr Snapshot Handler.
Fügen Sie diese Komponenten zu Ihrem Handler aus Schritt 1 hinzu:
- Aktivieren Sie ein Schattenmodus-Flag (fügen Sie es oben in Ihrer Datei hinzu):
// Enable shadow mode via environment variable const SHADOW_MODE = process.env.SHADOW_MODE !== 'false';
- Vollständige Ereignisdetails abrufen (nach Signaturprüfung hinzufügen):
// Fetch the full event details const event = await stripe.v2.core.events.retrieve(thinNotification.id); console.log(`Processing ${event.type} (thin ID: ${event.id})`); // For interop events, log the original snapshot event ID if (event.snapshot_event) { console.log(`Correlated snapshot event: ${event.snapshot_event}`); }
- Rufen Sie verwandte Objekte und Schattenprotokolle ab (Ereignisverarbeitungslogik hinzufügen):
// Fetch the related object if needed if (event.type === 'v1.customer.created') { const customer = await stripe.customers.retrieve(event.related_object.id); if (SHADOW_MODE) { // SHADOW MODE: Log what you would do, but don't do it yet console.log(`[SHADOW] Would create customer record: ${customer.id}`); // recordMetric('customer.created.thin', 1); } else { // Production mode: Actually write to database await createCustomerInDatabase(customer); } }
- Vervollständigen Sie den Handler mit dem Schattenmodus:
const SHADOW_MODE = process.env.SHADOW_MODE !== 'false'; // ← NEW app.post( '/webhook/thin', express.raw({type: 'application/json'}), async (req, res) => { const sig = req.headers['stripe-signature']; const thinWebhookSecret = process.env.THIN_WEBHOOK_SECRET; try { const thinNotification = stripe.webhooks.constructEvent( req.body, sig, thinWebhookSecret ); // ← NEW: Fetch full event details const event = await stripe.v2.core.events.retrieve(thinNotification.id); console.log(`Processing ${event.type} (thin ID: ${event.id})`); // ← NEW: Log snapshot event ID for correlation if (event.snapshot_event) { console.log(`Correlated snapshot event: ${event.snapshot_event}`); } // ← NEW: Fetch related object and handle in shadow mode if (event.type === 'v1.customer.created') { const customer = await stripe.customers.retrieve(event.related_object.id); if (SHADOW_MODE) { console.log(`[SHADOW] Would create customer record: ${customer.id}`); } else { await createCustomerInDatabase(customer); } } res.sendStatus(200); } catch (err) { console.log(`Webhook Error: ${err.message}`); res.status(400).send(`Webhook Error: ${err.message}`); } } );
Worauf Sie im Schattenmodus achten sollten
Führen Sie den Schattenmodus für mindestens 24-48 Stunden aus und überwachen Sie ihn:
- Signaturverifizierung: Bestätigen Sie, dass alle Thin-Ereignisse erfolgreich verifiziert wurden.
- Latenz abrufen: Messen Sie, wie lange es dauert, Ereignisse und zugehörige Objekte abzurufen.
- Verhaltenskonsistenz: Vergleichen Sie Schattenlogs mit dem tatsächlichen Verhalten Ihres Snapshot-Handlers.
- Fehlerquoten: Achten Sie auf unerwartete Fehler oder fehlende Daten.
Wenn die Schattenlogs Ihres Thin Handlers von den tatsächlichen Funktionen des Snapshot Handlers abweichen, untersuchen und beheben Sie die Abweichung jetzt (ohne Auswirkungen auf die Produktion).
Hinweis
Bei Interop-Ereignissen enthält das Feld snapshot_ die ursprüngliche Snapshot-Ereignis-ID. Protokollieren Sie sowohl die Thin--Ereignis-ID als auch das Ereignis event., um Ereignisse während des Übergangs zwischen beiden Handlern zu korrelieren.
Cutover mit Idempotenz durchführen
Wenn der Schattenmodus sauber ausgeführt wird, aktivieren Sie echte Schreibvorgänge in Ihrem Thin-Handler. Lassen Sie beide Ziele während eines kurzen Überlappungsfensters aktiv, um sicherzustellen, dass Sie keine Ereignisse verpassen.
Idempotenz implementieren
Während der Überschneidung erhalten Sie dasselbe logische Ereignis möglicherweise zweimal: einmal als Snapshot-Ereignis und einmal als Thin-Ereignis. Verwenden Sie Idempotenz-Schlüssel, um eine doppelte Verarbeitung zu verhindern.
Hinweis
Das Feld snapshot_ in Thin-Interop-Ereignissen enthält die ursprüngliche Snapshot-Ereignis-ID. Wenn Sie dieses Feld als Ihren Idempotenz-Schlüssel verwenden, können beide Handler mit demselben Schlüssel dedupliziert werden.
Richten Sie zunächst eine Idempotenz-Datenbanktabelle ein:
CREATE TABLE event_idempotency ( idempotency_key TEXT PRIMARY KEY, event_type TEXT NOT NULL, processed_at INTEGER NOT NULL );
Implementieren Sie als Nächstes den Idempotenz-Helper:
// Try to insert idempotency key. Returns true if successfully inserted, false if duplicate. function tryInsertIdempotencyKey(idempotencyKey, eventType) { try { db.prepare(` INSERT INTO event_idempotency (idempotency_key, event_type, processed_at) VALUES (?, ?, ?) `).run(idempotencyKey, eventType, Date.now()); return true; // Insert succeeded - event is new } catch (err) { if (err.code === 'SQLITE_CONSTRAINT') { return false; // Duplicate - event already processed (use your database-specific error code for unique constraint violations) } throw err; // Re-throw unexpected errors } }
Aktualisieren Sie beide Handler, um dieses Muster zu verwenden. Zunächst den Snapshot-Handler:
app.post( '/webhook/snapshot', express.raw({type: 'application/json'}), (req, res) => { const sig = req.headers['stripe-signature']; const snapshotWebhookSecret = process.env.SNAPSHOT_WEBHOOK_SECRET; try { const event = stripe.webhooks.constructEvent( req.body, sig, snapshotWebhookSecret ); // For snapshot events, idempotency key is just the event ID const idempotencyKey = event.id; // Try to insert idempotency key. If fails (duplicate), skip processing. if (!tryInsertIdempotencyKey(idempotencyKey, event.type)) { console.log(`Already processed: ${idempotencyKey}`); return res.sendStatus(200); } // Process the event if (event.type === 'customer.created') { createCustomerInDatabase(event.data.object); } res.sendStatus(200); } catch (err) { console.log(`Webhook Error: ${err.message}`); res.status(400).send(`Webhook Error: ${err.message}`); } } );
Als Nächstes der Thin-Handler:
const SHADOW_MODE = process.env.SHADOW_MODE !== 'false'; app.post( '/webhook/thin', express.raw({type: 'application/json'}), async (req, res) => { const sig = req.headers['stripe-signature']; const thinWebhookSecret = process.env.THIN_WEBHOOK_SECRET; try { const thinNotification = stripe.webhooks.constructEvent( req.body, sig, thinWebhookSecret ); const event = await stripe.v2.core.events.retrieve(thinNotification.id); // For thin interop events, use snapshot_event if present, otherwise event ID const idempotencyKey = event.snapshot_event || event.id; // Check idempotency first to avoid unnecessary work if (!SHADOW_MODE) { // Try to insert idempotency key. If fails (duplicate), skip processing. if (!tryInsertIdempotencyKey(idempotencyKey, event.type)) { console.log(`Already processed: ${idempotencyKey}`); return res.sendStatus(200); } } // Process the event if (event.type === 'v1.customer.created') { const customer = await stripe.customers.retrieve(event.related_object.id); if (!SHADOW_MODE) { // Process the event createCustomerInDatabase(customer); } else { console.log(`[SHADOW] Would create customer: ${customer.id}`); console.log(`[SHADOW] Idempotency key: ${idempotencyKey}`); } } res.sendStatus(200); } catch (err) { console.log(`Webhook Error: ${err.message}`); res.status(400).send(`Webhook Error: ${err.message}`); } } );
So verhindern Sie Duplikate:
Wenn eine Kundin/ein Kunde erstellt wird, generiert Stripe beide Ereignisse:
- Snapshot-Ereignis:
evt_abc123 - Thin-Ereignis:
evt_mitxyz789 snapshot_event: "evt_ abc123"
- Snapshot-Ereignis:
Beide Handler empfangen Ereignisse gleichzeitig:
- Snapshot-Handler:
idempotencyKey = event.>id "evt_abc123" - Thin Handler:
idempotencyKey = event.→snapshot_ event || event. id "evt_abc123"
- Snapshot-Handler:
Beide Handler versuchen, denselben Schlüssel einzufügen:
- Snapshot-Handler:
INSERT INTO .> erfolgreich. . VALUES ('evt_ abc123', . . . ) - Thin-Handler:
INSERT INTO .> eindeutige Einschränkungsverletzung. . VALUES ('evt_ abc123', . . . )
- Snapshot-Handler:
Ergebnis: Die Kundin/der Kunde wird erstellt.
Cutover überwachen
Wenn beide Handler in Ihre Datenbank schreiben:
- Duplikaterkennung beobachten: Bestätigen Sie, dass Ihre Idempotenz-Logik überlappende Ereignisse abfängt.
- Schreibmuster vergleichen: Stellen Sie sicher, dass Thin- und Snapshot-Handler identische Datenbankstatus erzeugen.
- Fehlerquoten verfolgen: Warnung bei erhöhtem Fehleraufkommen.
- Leistung überwachen: Messen Sie die Auswirkungen zusätzlicher API-Aufrufe, um verwandte Objekte abzurufen.
Warnung
Wenn etwas schief aussieht, deaktivieren Sie das Schreiben im Thin-Handler und untersuchen Sie es. Ihr Snapshot-Handler bleibt Ihr Bezugspunkt, bis Sie sich im Thin-Pfad sicher sind.
Halten Sie das Überlappungsfenster kurz (höchstens einige Stunden bis zu einem Tag). Dadurch wird der Zeitraum begrenzt, in dem Sie Ereignisse zweimal verarbeiten, und die Fehlerbehebung bei Problemen wird vereinfacht.
Snapshot-Ziel zurücksetzen
Nachdem der Thin-Handler Ereignisse zuverlässig für einen komfortablen Zeitraum verarbeitet hat, können Sie das Snapshot-Ziel zurücksetzen.
Phase 1: Deaktivieren (Code beibehalten)
- Gehen Sie im Stripe-Dashboard zu Entwickler > Webhooks > Ereignisziele.
- Suchen Sie Ihr Snapshot-Ereignisziel.
- Klicken Sie auf Deaktivieren.
Dadurch wird verhindert, dass Stripe Ereignisse an Ihren Snapshot-Endpoint sendet, aber Ihr Code bleibt als Sicherheitsmaßnahme bestehen. Überwachen Sie Ihren Thin-Only-Ablauf, um die Stabilität zu bestätigen.
Phase 2: Snapshot löschen
Wenn alles stabil bleibt:
- Löschen Sie das Snapshot-Ereignisziel aus dem Dashboard.
- Entfernen Sie den Handler-Code des Snapshot-Webhooks aus Ihrer Anwendung.
- Löschen Sie das
SNAPSHOT_aus Ihrer Konfiguration.Webhook_ SECRET - Aktualisieren Sie alle Dokumentationen oder Runbooks, die auf Snapshot-Ereignisse verweisen.
Fehlerbehebung
Thin-Handler empfängt keine Ereignisse
Überprüfen Sie Ihre Endpoint-URL: Überprüfen Sie die dünnen Zielpunkte auf die richtige URL (zum Beispiel /webhook/thin, nicht /webhook).
Lokal testen: Verwenden Sie ein Tunneling-Tool wie ngrok, um Ihren lokalen Endpoint anzuzeigen, und erstellen Sie dann ein dünnes Ereignisziel, das auf diese URL verweist.
Verifizierung der Signatur schlägt fehl
Webhook-Geheimnis prüfen: Stellen Sie sicher, dass THIN_ das Signaturgeheimnis von Ihrem Thin-Ereignisziel enthält, nicht Ihr Snapshot-Ziel.
Rohnutzlast prüfen: Die Signaturprüfung erfordert den Roh-Anfragetext. Parsen Sie das JSON nicht, bevor Sie Folgendes verifizieren:
// Correct: use express.raw() app.post('/webhook/thin', express.raw({type: 'application/json'}), handler); // Incorrect: express.json() parses the body app.post('/webhook/thin', express.json(), handler);
Empfehlungen
Nach Ereignistyp migrieren
Wir empfehlen Ihnen, Ereignis für Ereignis zu migrieren, indem Sie bestimmte Thin-Ereignistypen in Ihrem neuen Ziel abonnieren. Beginnen Sie beispielsweise mit v1. und v1., validieren Sie sie und fügen Sie dann weitere Ereignistypen hinzu.
Dual-Handler
Wir raten davon ab, Dual-Handler auszuführen oder sie auf unbestimmte Zeit ausführen zu lassen. Die Ausführung von Dual-Handlern erhöht die betriebliche Komplexität, die Kosten (Bandbreite und Verarbeitung) und erhöht das Risiko eines abweichenden Verhaltens. Schließen Sie die Migration innerhalb weniger Wochen ab.
API-Upgrades nach der Migration
Ein wesentlicher Vorteil von Thin-Ereignissen ist, dass sich Ihre Webhook-Nutzlast nicht ändert. Die Push-Methode bleibt stabil und Sie rufen versionierte Ressourcendetails ab, wenn Sie sie mit Ihrer aktuellen API-Version benötigen.