How I Would Fix webhooks failing silently in a Next.js and Stripe mobile app Using Launch Ready.
The symptom is usually simple on the surface: a payment succeeds, the app never updates, and support only finds out when a customer complains. In a...
How I Would Fix webhooks failing silently in a Next.js and Stripe mobile app Using Launch Ready
The symptom is usually simple on the surface: a payment succeeds, the app never updates, and support only finds out when a customer complains. In a Next.js and Stripe mobile app, the most likely root cause is not "Stripe is broken" but that the webhook endpoint is not being reached, is returning a non-2xx response, or is failing signature verification before your code ever runs.
The first thing I would inspect is the full webhook path end to end: Stripe event delivery logs, the Next.js route handler or API route, deployment logs, and any proxy or Cloudflare rules sitting in front of the app. Silent failures are usually an observability problem first, then a code problem.
Triage in the First Hour
1. Check Stripe Dashboard > Developers > Webhooks.
- Look at recent events, delivery attempts, response codes, and retry history.
- Confirm whether Stripe says "Delivered" even if your app did nothing useful.
2. Open the exact webhook endpoint in production.
- Verify the URL matches production, not localhost or a stale preview domain.
- Confirm it uses HTTPS and resolves correctly through Cloudflare or your host.
3. Inspect server logs for the webhook route.
- Look for request hits, 400s, 401s, 403s, 404s, 500s, and timeouts.
- If there are no logs at all, traffic may be blocked before it reaches Next.js.
4. Check deployment environment variables.
- Confirm `STRIPE_WEBHOOK_SECRET`, Stripe API keys, and app URLs are set in production.
- Make sure preview and production secrets are not mixed up.
5. Review the webhook handler code.
- Verify raw body handling is correct for Stripe signature verification.
- In Next.js this is where many silent failures happen after refactors.
6. Check Cloudflare rules and caching settings.
- Ensure `/api/stripe/webhook` or similar routes are not cached or challenged.
- Disable bot protection or WAF rules for that endpoint if they block POST requests.
7. Inspect mobile app state changes that depend on webhooks.
- Confirm the app polls or refreshes after payment instead of assuming instant webhook completion.
- If users see "pending" forever, you may also have a UX fallback issue.
8. Check alerting and uptime monitoring.
- If you had no alert when webhooks stopped firing, that is part of the failure.
- Silent failure becomes expensive when refunds, churn, and support tickets pile up.
## Quick local check for route availability curl -i https://yourdomain.com/api/stripe/webhook ## Stripe CLI test event stripe listen --forward-to localhost:3000/api/stripe/webhook stripe trigger payment_intent.succeeded
Root Causes
| Likely cause | How to confirm | What it means | |---|---|---| | Wrong webhook secret | Signature verification fails in logs | The endpoint receives events but rejects them | | Raw body parsing issue | Handler uses JSON parsing before verification | Stripe signatures cannot be validated | | Bad deployment URL | Stripe dashboard points to old domain or preview URL | Events go to the wrong place | | Cloudflare or WAF blocking | No request reaches app logs; Cloudflare shows challenge/block | The request dies before Next.js sees it | | Handler returns non-2xx | Stripe retries with error responses in dashboard | Your code errors after receiving the event | | Idempotency missing | Duplicate events create inconsistent state | Retries or replayed events corrupt data |
1. Wrong webhook secret
This happens when production is using a test secret or an old rotated secret. I would confirm by comparing the secret stored in your deployment platform with the signing secret shown in Stripe Dashboard for that exact endpoint.
If signature verification fails, fix the environment variable first. Do not patch around it by disabling verification; that creates a security hole.
2. Raw body parsing issue
Stripe needs the raw request body for signature verification. In Next.js apps, especially after moving from pages router to app router, developers often accidentally parse JSON too early.
I would confirm by checking whether your route reads `req.body` as parsed JSON before calling `stripe.webhooks.constructEvent(...)`. If yes, that is likely your bug.
3. Bad deployment URL
Mobile apps often use different environments for staging and production. A webhook can be configured against a preview domain that was later deleted or replaced during deploys.
I would confirm by checking every active webhook endpoint in Stripe and matching it to live DNS records. If there are multiple endpoints with overlapping events, clean that up now.
4. Cloudflare or WAF blocking
Cloudflare can protect you from abuse, but it can also block legitimate POST requests if rules are too aggressive. This shows up as missing logs in your app and blocked requests in Cloudflare analytics.
I would confirm by checking firewall events and turning off challenge behavior for the webhook path only. Do not disable protection site-wide just to make one endpoint work.
5. Handler returns non-2xx
Stripe treats anything outside 200 range as failed delivery and retries later. If your handler throws after writing part of its work to the database, you get partial side effects plus retries.
I would confirm by looking at response codes in Stripe delivery logs and application errors around database writes, auth checks, or missing fields.
6. Idempotency missing
Stripe retries events more than once by design. If your code creates duplicate records or sends repeated push notifications without checking event IDs, silent failure turns into noisy data corruption.
I would confirm by searching your database for repeated `event.id` values or duplicate side effects tied to one payment event.
The Fix Plan
First I would make the webhook path boring and explicit. That means one dedicated route for Stripe webhooks with no extra business logic beyond validation, persistence of event metadata, and dispatching downstream work safely.
Second I would fix signature verification using raw body handling appropriate to your Next.js setup. In most cases I prefer keeping this endpoint isolated from general middleware so auth checks, body parsers, redirects, and caching do not interfere with delivery.
Third I would add idempotency at the database layer. Store each processed `event.id` with a unique constraint so retries cannot create duplicate updates or duplicate notifications.
Fourth I would separate "receive" from "process". The webhook should acknowledge quickly with a 2xx after validating and queueing work; heavier logic like subscription syncs or receipt generation should run asynchronously so p95 response time stays under 200 ms on webhook intake.
Fifth I would remove anything that can interrupt POST delivery:
- No redirects on the webhook route
- No page-level middleware on this path unless explicitly allowed
- No caching headers
- No auth prompts
- No Cloudflare challenge pages
Sixth I would add structured logging around every step:
- Request received
- Signature verified
- Event type detected
- Event stored
- Downstream job queued
- Response sent
That gives you evidence when something breaks again instead of another silent outage.
A safe implementation pattern looks like this:
// Pseudocode only: keep raw body intact for Stripe signature verification
export const config = { api: { bodyParser: false } };
if (req.method !== "POST") return res.status(405).end();
const sig = req.headers["stripe-signature"];
const rawBody = await getRawBody(req);
let event;
try {
event = stripe.webhooks.constructEvent(rawBody, sig!, process.env.STRIPE_WEBHOOK_SECRET!);
} catch (err) {
console.error("Webhook signature failed", err);
return res.status(400).send("Invalid signature");
}
// Save event.id first to prevent duplicates
// Queue background processing here
return res.status(200).json({ received: true });Finally I would verify all related production settings:
- Correct domain DNS records
- SSL active on every relevant subdomain
- Environment variables present in production only where needed
- Monitoring enabled on both uptime and error rate
- Email authentication set up if webhooks trigger transactional mail flows
Regression Tests Before Redeploy
Before shipping anything back to users, I want these checks passing:
1. Delivery test from Stripe CLI.
- Trigger at least three event types used by your app.
- Confirm each one reaches production-like behavior without manual intervention.
2. Signature failure test.
- Send an invalid signature once.
- Acceptance criteria: request returns 400 and does not write bad data.
3. Duplicate event test.
- Replay the same `event.id`.
- Acceptance criteria: only one record is created and downstream actions run once.
4. Failure recovery test.
- Force a temporary downstream DB error.
- Acceptance criteria: endpoint logs clearly show failure and retry behavior is understood.
5. Mobile flow validation.
- Complete payment on device emulator and real device if possible.
- Acceptance criteria: user sees correct status within 10 seconds after payment completion or gets a clear pending state with refresh behavior.
6. Deployment smoke test.
- After deploy verify endpoint responds correctly from production domain over HTTPS.
- Acceptance criteria: no redirect loops, no Cloudflare blocks, no 5xx responses.
7. Security checks.
- Confirm secrets are not exposed in client bundles or logs.
- Acceptance criteria: no secret values appear in browser devtools network payloads or public repo files.
8. Observability check.
- Confirm alerts fire on repeated failures within 5 minutes.
- Acceptance criteria: at least one alert channel catches sustained webhook errors before customers do.
Prevention
I would put three guardrails in place so this does not happen again:
1. Monitoring first.
- Alert on non-2xx responses from webhook routes.
- Alert on zero deliveries over a rolling window if payments are still happening.
- Track p95 latency under 200 ms for intake routes so slow processing does not hide problems until retries start piling up.
2. Code review standards for webhooks.
- Require raw-body handling review before merge.
- Require idempotency checks on every external event handler.
- Require explicit logging for success and failure paths.
3. Security controls based on API security best practices.
- Keep signature verification mandatory for all Stripe callbacks.
- Limit who can change webhook URLs in production accounts.
- Rotate secrets carefully and document which environment uses which key pair.
From a UX angle, do not leave users staring at uncertainty after payment submission. Show "processing" states clearly on mobile screens with refresh behavior so support does not absorb every delay caused by third-party retries or network issues.
From a performance angle, keep this route thin enough that it does one job well: validate fast, persist safely, hand off work immediately. That reduces downtime risk during traffic spikes and lowers support load when something upstream gets slow.
When to Use Launch Ready
I use it when domain setup, email deliverability, Cloudflare rules,, SSL,, secrets,, deployment,, monitoring,, or broken backend handoff are blocking launch more than product design itself.,,
In this case I would use Launch Ready to:
- Fix DNS records and redirects
- Set up Cloudflare correctly for API routes
- Verify SSL across domains and subdomains
- Audit environment variables and secrets handling
- Turn on uptime monitoring and basic alerting
- Hand over a checklist so your team knows what was changed
What you should prepare:
- Production repo access
- Deployment platform access
- Stripe dashboard access
- Cloudflare access if used
- A list of all environments: local,, staging,, production,, preview
- Any recent screenshots or error reports from users
If you already have working code but payments are behaving unreliably,, this sprint is cheaper than losing another week of revenue to hidden failures.,,
Delivery Map
References
1. Roadmap.sh API Security Best Practices: https://roadmap.sh/api-security-best-practices 2. Roadmap.sh QA: https://roadmap.sh/qa 3. Roadmap.sh Cyber Security: https://roadmap.sh/cyber-security 4. Stripe Webhooks Docs: https://docs.stripe.com/webhooks 5. Next.js Route Handlers Docs: https://nextjs.org/docs/app/building-your-application/routing/route-handlers
---
Take the next step
If this is a problem in your product right now, here is what to do next:
- [Use the free Cyprian tools](/tools) - estimate cost, score app risk, check launch readiness, or pick the right service sprint.
- [Book a discovery call](/contact) - I will tell you honestly whether you need a sprint or if you can DIY the next step.
*Written by Cyprian Tinashe Aarons - senior full-stack and AI engineer helping founders rescue, launch, automate, and scale AI-built products.*
Cyprian Tinashe Aarons — Senior Full Stack & AI Engineer
Cyprian helps founders rescue, secure, deploy, and automate AI-built apps with production-grade engineering, launch systems, and AI integration.