Webhook retry logic is handled with idempotent processing design
Why it matters
Stripe retries webhook delivery up to 3 days when it doesn't receive a 200 response within 30 seconds. If your webhook handler performs a plain db.orders.insert() keyed only on customer data — not on event.id or paymentIntent.id — every retry creates a duplicate order record, triggers a duplicate fulfillment, and sends the customer a duplicate confirmation email. CWE-362 (Race Condition) applies when two concurrent deliveries of the same event race to insert the same order. This is not a theoretical failure: network blips during high-traffic events (flash sales, holiday peaks) cause Stripe retries at exactly the moment your database is under maximum load.
Severity rationale
Low because non-idempotent webhook handlers create duplicate orders and emails on every Stripe retry, with retry frequency increasing precisely when database load causes initial delivery to timeout.
Remediation
Deduplicate by recording processed event IDs in a database table before executing business logic.
export async function POST(req: Request) {
// ... signature verification ...
await db.$transaction(async (tx) => {
// Mark the event as processed first — unique constraint prevents double-processing
await tx.processedWebhookEvents.create({ data: { stripeEventId: event.id } })
switch (event.type) {
case 'payment_intent.succeeded':
// Upsert instead of insert for additional safety
await tx.orders.upsert({
where: { stripePaymentIntentId: event.data.object.id },
create: { /* order fields */ },
update: { status: 'confirmed' },
})
break
}
})
return new Response('OK', { status: 200 })
}
Add a unique index on processed_webhook_events.stripe_event_id. The unique constraint causes the second delivery's create to throw, which is caught by the $transaction and aborted cleanly.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Payment Security Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-362 — Concurrent Execution Using Shared Resource with Improper Synchronization (Race Condition)
- iso-25010:2011 · reliability.fault-tolerance
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ecommerce-payment-security·automated