
Bun.js + Stripe Webhooks + Drizzle: Zahlungen ohne Duplikate
Stripe-Webhooks mit Bun.js sicher verarbeiten: Signaturprüfung, PostgreSQL-Event-Ledger, Idempotenz und transaktionssichere Geschäftslogik.
Bun liest den Raw Body, das Stripe SDK prüft die Signatur und PostgreSQL speichert die Event-ID vor der Geschäftslogik. Ein Duplikat wird im Ledger erkannt und als No-op beendet.
Warum ein Webhook kein normaler Callback ist
Stripe kann Zustellung wiederholen, Events in anderer Reihenfolge liefern oder nach einem temporären Serverfehler erneut senden.
01
Stripe sendet ein signiertes Event
Der Payload enthält Stripe-Signature. Vor der Prüfung darf JSON nicht vertraut werden.
02
Bun prüft den Raw Body
request.text() statt request.json() lesen: Parsing kann signierte Bytes verändern.
03
Das Ledger erkennt Duplikate
Eine eindeutige event.id verhindert doppelte Geschäftsoperationen.
04
Eine Transaktion ändert die Domain
Payment-Status oder Entitlement ändern und Event atomar als verarbeitet markieren.
Die doppelte Zustellung stoppt bei Idempotency; der Business-Job läuft erst nach der Prüfung.
Screenshot des Abschnitts architectureSchritt 1: SDK und Secrets
SDK installieren
bun add stripe drizzle-orm postgres
bun add -d drizzle-kitSecrets konfigurieren
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
DATABASE_URL=postgres://app:app@localhost:5432/paymentsDas Secret der Stripe CLI und das Production-Secret sind verschieden.
Event-Ledger anlegen
export const stripeEvents = pgTable("stripe_events", {
id: text("id").primaryKey(),
type: text("type").notNull(),
processedAt: timestamp("processed_at", { withTimezone: true }),
receivedAt: timestamp("received_at", { withTimezone: true }).defaultNow().notNull(),
});Der Primary Key ist die letzte Schutzschicht gegen Duplikate.
Schritt 2: Raw Body signieren
Rufe request.json() nicht vor constructEvent auf. Nach dem Parsing können die Bytes vom signierten Payload abweichen.
import Stripe from "stripe";
const stripe = new Stripe(Bun.env.STRIPE_SECRET_KEY!);
export async function verifyStripeRequest(request: Request) {
const signature = request.headers.get("stripe-signature");
if (!signature) throw new Response("Missing signature", { status: 400 });
const rawBody = await request.text();
return stripe.webhooks.constructEvent(rawBody, signature, Bun.env.STRIPE_WEBHOOK_SECRET!);
}constructEvent prüft HMAC-Signatur und Zeitfenster. Alles andere ablehnen.
Schritt 3: idempotenter Drizzle-Handler
Füge das Event zuerst ein. Ein Conflict bedeutet eine erneute Zustellung — die Geschäftslogik darf nicht erneut laufen.
export async function processEvent(event: Stripe.Event) {
const inserted = await db.insert(stripeEvents).values({ id: event.id, type: event.type }).onConflictDoNothing().returning();
if (inserted.length === 0) return { duplicate: true };
await db.transaction(async (tx) => {
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
// Bestellung auf paid setzen, z. B. mit tx.update(...).
}
await tx.update(stripeEvents).set({ processedAt: new Date() }).where(eq(stripeEvents.id, event.id));
});
return { duplicate: false };
}Schritt 4: Endpoint und Retry-Policy
Gib 2xx erst zurück, wenn das Event geprüft und verarbeitet wurde oder schon im Ledger steht. Bei einem temporären Datenbankfehler muss Stripe mit 500 erneut senden.
export async function POST(request: Request) {
try {
const event = await verifyStripeRequest(request);
await processEvent(event);
return new Response("ok", { status: 200 });
} catch (error) {
console.error("stripe webhook failed", error);
return new Response("retry", { status: 500 });
}
}Langsame Jobs nach dem Commit in eine Queue stellen; keine unbegrenzten Network Calls im Webhook-Request.
Lokal mit der Stripe CLI testen
Events weiterleiten
stripe listen --forward-to http://localhost:3000/api/stripe/webhookEvent auslösen
stripe trigger checkout.session.completedDuplikat prüfen
Dasselbe Event zweimal senden: eine Geschäftsänderung und danach { duplicate: true }. Signaturprüfung niemals in CI deaktivieren.
Production-Checkliste
Signatur und Zeitstempel prüfen
Frontend-Daten oder Event-Typ allein beweisen keine Zahlung.
Payload schützen
JSON nur für Audit speichern, Zugriffe begrenzen und Secrets aus Logs entfernen.
Webhook-Secrets rotieren
Separate Secrets pro Umgebung und definierte Rotation verwenden.
Fehler überwachen
Auf 5xx, steigende Duplikate und unverarbeitete Ledger-Zeilen alarmieren.
FAQ
Nicht vor der Signaturprüfung. Zuerst request.text() lesen, mit dem Stripe SDK prüfen und erst dann Event-Daten verwenden.
In der Regel 200, weil es bereits verarbeitet wurde. Bei einem temporären Datenbankfehler 500 zurückgeben, damit Stripe erneut zustellt.
Fazit
Beschreiben Sie die Aufgabe — die ersten 15 Minuten der Beratung sind kostenlos.
Verwandte Artikel

AI Assistant Entwicklung Kosten 2026: RAG, Knowledge Base, Integrationen und Support
Praktischer Leitfaden zu Kosten fuer AI Assistants: RAG, Knowledge Base, Channels, Tool Use, Guardrails, Evaluations, Monitoring und Support.

KI kann mehr erzeugen. Nicht besser: Was die Forschung über Spieleentwicklung sagt
Generative KI kommt in die Spieleproduktion, doch die Fakten sind nuancierter als der Hype. Wir betrachten Adoption, Spielervertrauen, Qualitätsrisiken und einen sinnvollen Workflow.

KI fur Landingpage-Entwicklung: wo sie Launches beschleunigt und wo sie Conversion schadet
Eine praxisnahe Analyse zur Nutzung von KI fur Landingpages: v0, Webflow AI, Builder.io, Framer-ahnliche Builder, UX-Generierung, Copy, SEO, Personalisierung, A/B-Tests, Template-Risiken, Accessibility, Security und technischer Schuldenaufbau.

AI SEO / GEO im Jahr 2026: Ihre nächsten Kunden sind nicht Menschen — sondern Agents
Suche verschiebt sich von Klicks zu Antworten. Bots und AI-Agents crawlen, zitieren, empfehlen — und kaufen zunehmend. Erfahren Sie, was AI SEO / GEO bedeutet, warum klassisches SEO nicht mehr reicht und wie PAS7 Studio Marken im agentischen Web sichtbar macht.
Professionelle Entwicklung für Ihr Geschäft
Wir erstellen moderne Web-Lösungen und Bots für Unternehmen. Erfahren Sie, wie wir Ihnen helfen können, Ihre Ziele zu erreichen.