惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
N
News and Events Feed by Topic
D
DataBreaches.Net
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Engineering at Meta
Engineering at Meta
T
Tailwind CSS Blog
博客园_首页
Microsoft Azure Blog
Microsoft Azure Blog
Y
Y Combinator Blog
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
月光博客
月光博客
A
About on SuperTechFans
I
InfoQ
S
Securelist
Last Week in AI
Last Week in AI
S
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN
Schneier on Security
Schneier on Security
Know Your Adversary
Know Your Adversary
腾讯CDC
大猫的无限游戏
大猫的无限游戏
S
Security @ Cisco Blogs
博客园 - 三生石上(FineUI控件)
Simon Willison's Weblog
Simon Willison's Weblog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tor Project blog
美团技术团队
aimingoo的专栏
aimingoo的专栏
G
Google Developers Blog
罗磊的独立博客
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Cloudflare Blog
S
Secure Thoughts
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Latest news
Latest news
Recent Announcements
Recent Announcements
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
LINUX DO - 热门话题
Security Latest
Security Latest
TaoSecurity Blog
TaoSecurity Blog
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Why your Stripe webhook might be silently dropping events (and how to find out)
Arseni · 2026-05-19 · via DEV Community

If you process Stripe subscriptions and have never opened Workbench → Webhooks → Event deliveries (or the older Developers → Webhooks → Attempts view), there's a chance you have failed deliveries from the last 30 days you don't know about. This is worth checking, so let's go through what to look for.

How Stripe delivers events

When a customer's payment fails, Stripe sends a POST to your webhook endpoint with an invoice.payment_failed event. Here's what happens next:

  • Your endpoint should quickly return a 2xx response. Stripe doesn't publish an exact timeout in seconds, but the docs say "quickly return 2xx prior to any complex logic".
  • If you time out, return non-2xx, or the endpoint is unreachable, Stripe marks the attempt as failed. Any non-2xx triggers retries, including 4xx — a common myth is that Stripe only retries on 5xx, but the docs say "if your endpoint previously replied with a non-2xx status code".
  • Retries happen with exponential backoff. In live mode, for up to 3 days. In sandbox/test mode, 3 attempts over a few hours. Stripe doesn't publish the exact retry schedule in the docs, but in practice it works out to roughly 16 attempts across those 3 days (first retry within an hour, then ~12 hours, then daily). You can see the next scheduled retry per event in the Event deliveries tab.
  • After ~3 days of continuous failures, Stripe disables your endpoint automatically and emails you. After disabling, new events are not delivered to that endpoint at all until you re-enable it. This is the single biggest source of "tiny drops" turning into "we lost a whole week of payment_failed events" — the retries didn't just expire, the endpoint went dark.
  • If all retries fail across the 3-day window (but the endpoint is still active), the event is not lost: you can manually resend it within 30 days via Stripe CLI, or within 15 days via Dashboard.

The catch: "not lost" only works if you know the event came in and was failed. If you don't check Event deliveries regularly, you find out about misses when a customer messages "why did you charge me, I cancelled a month ago" or when a monthly churn audit shows a hole in your MRR.

What actually causes events to get lost

Something I underestimated at first: Stripe retries for 3 days. That means a short 30-second deploy window on its own won't cause loss. Stripe will try again in an hour, in a few hours, the next day. You have a huge window to get the endpoint back up.

Real losses come from persistent problems that outlast the retry window — or worse, problems that get the endpoint auto-disabled. Here they are:

1. Endpoint persistently broken. Most common in my experience. For example, you rotated the Stripe webhook secret on your app side but didn't update it in the Stripe Dashboard. Every event arrives, signature verify fails, you return 400. Over 3 days, all retry attempts fail the same way, then Stripe disables the endpoint. After that, new events stop coming entirely. If you're not looking at Event deliveries, you don't find out until someone complains.

2. Firewall blocking Stripe IPs. Cloudflare WAF, AWS security groups, corporate firewalls — any of them can start dropping legitimate Stripe requests after a rules change. Stripe sends requests from a pool of IP addresses that changes over time. If you accidentally whitelisted only the old set, new IPs start receiving 403, and you don't see it. Stripe itself recommends against relying on IP allowlisting alone for this reason, and pairs it with signature verification.

3. Endpoint URL changed. You moved from /api/webhook to /v2/webhook or switched domains, but didn't update the URL in Stripe Dashboard. All events go to the old URL, get 404, retry for 3 days, endpoint gets disabled. I've watched a teammate ship this exact change on a Friday afternoon and find out on Monday.

4. Endpoint disabled or deleted at the moment of retry. If your endpoint is disabled or deleted when Stripe attempts a retry, that specific event is dropped. The nuance: if you disable and then re-enable the endpoint before the next retry attempt, future retries continue normally. The real risk is being disabled across the entire retry window — for example, you manually disabled an endpoint to fix something and forgot to turn it back on.

5. Sustained DB connection pool exhaustion. Not a one-minute spike, but constant load where the handler regularly waits for a DB connection longer than Stripe's timeout. If that happens for more than 3 days, events drop and the endpoint gets auto-disabled. Short-term exhaustion is usually covered by retry.

Short deploy windows and one-off OOM kills rarely cause loss precisely because the retry window is long. The dangerous ones are persistent problems your health checks don't catch — especially the ones that escalate into auto-disable.

How to check how much you're losing

Workbench → Webhooks → pick a specific endpoint → Event deliveries tab. (If you're still on the older Developers Dashboard, it's Developers → Webhooks → your endpoint → Attempts. Stripe is migrating everyone to Workbench, but both still work.) You'll see recent delivery attempts with success/failed status and response codes.

What to look for:

  • failed status over the last 30 days
  • Grouped by event type — which events fail more often
  • Time pattern — are fails clustered during deploy windows?
  • Any "endpoint disabled" notification emails sitting in your inbox that you ignored

Next, compare against your own log of received webhooks. If you're not logging each webhook receipt separately, start now. Minimal middleware:

app.post('/webhook/stripe', (req, res) => {
  console.log('webhook received', {
    event_id: req.body.id,
    event_type: req.body.type,
    livemode: req.body.livemode,
    received_at: new Date().toISOString()
  })
  // ... handler
})

Enter fullscreen mode Exit fullscreen mode

A week later, compare event counts in Stripe Dashboard vs your log. The diff is your drop rate. If it's zero, great. If it's non-zero, you now have a concrete number and a list of specific event_ids that got dropped, and you can dig into why.

What to do to stop losing events

The main pattern: ACK fast, process async. Your webhook handler should do the minimum work and return 200 immediately. Heavy processing goes into a background queue.

app.post('/webhook/stripe', async (req, res) => {
  // 1. verify signature
  const event = stripe.webhooks.constructEvent(
    req.rawBody,
    req.headers['stripe-signature'],
    process.env.STRIPE_WEBHOOK_SECRET
  )

  // 2. persist for idempotency (event.id is the right key — Stripe
  //    explicitly recommends "logging the event IDs you've processed"
  //    to guard against duplicates from retries and manual resends)
  await db.insert(stripe_events).values({
    id: event.id,
    type: event.type,
    payload: event,
    received_at: new Date()
  }).onConflictDoNothing()

  // 3. enqueue and respond immediately
  await queue.add('process-stripe-event', { event_id: event.id })

  res.status(200).send('ok')
})

Enter fullscreen mode Exit fullscreen mode

What this gives you:

  • The handler returns 200 in milliseconds
  • Even if downstream processing falls over in the queue, the event is in your DB
  • Using event.id as the idempotency key lets you safely replay

Replay missed events. If you find failed events in the Dashboard, two ways to resend them:

  • Dashboard: open a specific event → Resend button. Available for 15 days after the event is created.
  • Stripe CLI: stripe events resend <event_id> --webhook-endpoint=<endpoint_id>. Available for 30 days after the event is created.

Important nuance from the Stripe docs: a manual resend does not cancel the automatic Stripe retry cycle. So your endpoint may receive the same event twice, once from Stripe's retry, once from your manual resend. That's why an idempotency check on event.id is mandatory.

Reconcile via the Events API. Stripe itself recommends not relying solely on webhook delivery for critical data, and has a dedicated guide for this called Process undelivered webhook events. The pattern: periodically call the List Events API filtered by event type and time window, and compare against what your DB has. For example, to find invoice.payment_failed events in a window:

GET /v1/events?type=invoice.payment_failed&created[gte]=<unix_ts>&created[lt]=<unix_ts>

Enter fullscreen mode Exit fullscreen mode

This is more reliable than hoping every webhook made it through. For a production billing system, running this nightly as a safety net is cheap insurance.

Email notifications about failures. Stripe already sends these automatically when your endpoint is failing or gets disabled — you don't need to enable anything. What you do need to check:

  • That those emails are going to a human who actually reads them (Settings → Team and Security)
  • That they're not silently routed to spam
  • That the recipient knows what to do when one arrives

I've seen teams where the webhook-failure emails were going to a shared noreply@ inbox no one had opened in 8 months.

Optional: independent alerting. On top of Stripe's emails, you can:

  • Run a third-party health check (UptimeRobot, Better Stack) on the webhook URL itself for response time.
  • Push your own webhook-received metric to Datadog/Grafana and alert when failed_attempts > N in a window.

The point

invoice.payment_failed events matter more than most of your GitHub PRs and Jira issues, yet they usually get the least monitoring. Open Stripe Dashboard now and look at Event deliveries for the last 30 days. If there are failed records you've never seen, you have a gap in your data worth closing.

The short version: check your Event deliveries tab, make sure your handler is ACK-fast with async processing and idempotency on event.id, and run a nightly reconcile against the List Events API for the events that matter most. That closes 90% of the nastiest cases where events go silent.


P.S. I'm building a small Telegram bot called devpinger that delivers engineering alerts to one chat — GitHub PRs, CI failures, Jira issues already work; Stripe webhook failures and Sentry alerts are next. MIT, in case it's useful.