How I Would Fix webhooks failing silently in a Next.js and Stripe marketplace MVP Using Launch Ready.
The symptom is usually ugly: payments look successful in Stripe, but orders never appear, seller balances do not update, or the buyer sits on a...
How I Would Fix webhooks failing silently in a Next.js and Stripe marketplace MVP Using Launch Ready
The symptom is usually ugly: payments look successful in Stripe, but orders never appear, seller balances do not update, or the buyer sits on a "processing" screen forever. In a marketplace MVP, that is almost always a webhook delivery or webhook handling problem, not a checkout problem.
The most likely root cause is one of these: the endpoint is not reachable in production, the raw request body is being mutated before signature verification, or the app is returning 200 too early and then failing after the response. The first thing I would inspect is the Stripe dashboard event log for failed deliveries, then the Next.js server logs around the webhook route, and then the exact route implementation to confirm signature verification is happening on the raw body.
Triage in the First Hour
1. Open Stripe Dashboard > Developers > Webhooks.
- Check recent events.
- Look for retries, 4xx, 5xx, or timeouts.
- Confirm whether Stripe thinks it delivered the event.
2. Inspect the specific failed event payload.
- Compare `checkout.session.completed`, `payment_intent.succeeded`, or `invoice.paid` against what your app expects.
- Make sure you are listening for the right event type for your marketplace flow.
3. Check your production logs first.
- Vercel logs, Render logs, Fly.io logs, or your Node process logs.
- Search for webhook route hits and errors within the same minute as Stripe delivery attempts.
4. Verify the webhook URL in Stripe.
- Confirm it points to production, not localhost.
- Confirm there are no stale preview URLs from old deployments.
5. Inspect the Next.js route file.
- Look for JSON body parsing before Stripe signature verification.
- Look for use of App Router vs Pages Router mismatch.
- Check whether `export const runtime = "nodejs"` is set if needed.
6. Check environment variables in production.
- `STRIPE_SECRET_KEY`
- `STRIPE_WEBHOOK_SECRET`
- Any marketplace account IDs or connected account settings
- Make sure they exist in the deployed environment, not only locally.
7. Review deployment status and recent changes.
- Did this break after a refactor?
- Did someone move from Pages Router to App Router?
- Did a new middleware or auth layer start intercepting `/api/webhook`?
8. Test with a known Stripe CLI event if local tunneling is available.
- This confirms whether your handler works at all before you touch production.
9. Check Cloudflare or proxy rules if traffic passes through them.
- Webhooks can fail silently when WAF rules block POSTs or when caching interferes with route behavior.
10. Confirm idempotency and event storage behavior.
- If events are processed twice or dropped after partial failure, you may see inconsistent state instead of obvious errors.
stripe listen --forward-to localhost:3000/api/stripe/webhook stripe trigger checkout.session.completed
Root Causes
| Likely cause | How to confirm | Business impact | |---|---|---| | Raw body gets parsed before signature verification | The route uses `req.json()` or middleware mutates request content before `stripe.webhooks.constructEvent()` | Stripe rejects valid events, so fulfillment never happens | | Wrong webhook secret | Production env var does not match the endpoint secret in Stripe | Every delivery fails signature check | | Wrong event type | The code listens for `payment_intent.succeeded` but marketplace logic depends on `checkout.session.completed` or connected account events | Payment succeeds but downstream logic never runs | | Route unreachable or blocked | Stripe dashboard shows timeouts, 404s, 401s, or Cloudflare blocks | Events never reach your app | | Handler throws after partial work | Logs show database write failure, missing record lookup, or null reference error | Event may retry and create duplicate risk if not idempotent | | Connected accounts misconfigured | Marketplace uses Stripe Connect but webhook listens only on platform account events | Seller payout state stays broken |
1. Raw body parsing issue
This is the classic Next.js and Stripe bug. Stripe signature verification requires the exact raw payload bytes that Stripe sent.
I would confirm this by checking whether the code uses `req.json()`, `NextRequest`, body parser middleware, or any transform before verification. If yes, that is likely breaking signatures.
2. Secret mismatch
If staging and production secrets were copied around manually, there is a high chance the deployed app has an old webhook secret. I would compare the secret in Stripe's dashboard with the exact environment variable value in production.
3. Wrong event selection
A marketplace MVP often needs more than one event:
- Checkout completion for order creation
- Payment success for payment confirmation
- Connected account events for seller payout flow
If you listen to the wrong event, nothing looks broken at checkout but business logic never runs.
4. Middleware or Cloudflare interference
Auth middleware can accidentally protect webhook routes. Cloudflare WAF can also block requests that look unusual because they are signed POSTs from Stripe and not browser traffic.
I would inspect access logs and any edge rules before changing application code.
5. Hidden runtime mismatch
Some Next.js webhook handlers only work in Node runtime because they depend on raw request streams and Node libraries. If this route was deployed to an edge runtime by accident, webhook handling can fail in ways that are hard to spot quickly.
The Fix Plan
First I would stop guessing and make the failure visible end to end.
1. Isolate the webhook route.
- Put it behind a dedicated unauthenticated path like `/api/stripe/webhook`.
- Remove any middleware that protects this route unless it explicitly allows Stripe through.
2. Force Node runtime if needed.
- For App Router handlers that use Stripe signature verification, I would make sure this route runs in Node runtime unless there is a proven edge-compatible implementation.
3. Verify against raw body only.
- Do not parse JSON before signature validation.
- Read exactly what Stripe sent, then construct the event using the endpoint secret.
4. Log safely but clearly.
- Log event type, event ID, request timestamp, and processing result.
- Do not log full card data or sensitive customer details.
- Add error logging with enough context to debug retries without exposing secrets.
5. Make processing idempotent.
- Store processed event IDs in your database.
- Reject duplicates gracefully so retries do not create duplicate orders or payouts.
6. Split receipt from processing only if necessary.
- Return 200 only after durable enqueueing or successful processing of critical state changes.
- If processing takes too long, enqueue a job and acknowledge receipt after persistence.
7. Add explicit failure handling.
- If order creation fails because a related record does not exist yet, store a retryable state instead of swallowing it.
- Surface dead-letter cases to admin review instead of hiding them.
8. Re-test connected account flow separately if this is a marketplace.
- Platform webhooks and connected account webhooks behave differently.
- I would confirm which account owns each relevant event before shipping anything else.
A safe implementation pattern looks like this:
// Example shape only: verify raw body first
const sig = req.headers.get("stripe-signature");
const rawBody = await req.text();
const event = stripe.webhooks.constructEvent(rawBody, sig!, process.env.STRIPE_WEBHOOK_SECRET!);9. Deploy as a small safe change.
- Do not bundle this with UI updates or checkout redesigns.
- Ship just webhook repair plus logging plus idempotency checks so rollback stays simple.
Regression Tests Before Redeploy
I would not redeploy until these pass:
1. Signature verification test
- A real signed test event from Stripe CLI validates successfully in staging and production-like environments.
2. Wrong secret test
- Deliberately use an invalid secret in staging once to confirm failures are visible and logged correctly.
3. Duplicate delivery test
- Send the same event twice and confirm only one order or payout record is created.
4. Missing dependency test
- Simulate missing linked order/customer records and confirm the system handles it without crashing silently.
5. Event routing test
- Confirm each required event type maps to exactly one business action:
- checkout completion creates pending order
- payment success marks paid
- refund updates state if supported
6. Deployment smoke test
- After deploy, trigger one live test mode webhook from Stripe Dashboard and verify:
- dashboard shows delivered
- app logs show processed
- database row updated
- user-facing status changed within 30 seconds
7. Security checks
- Webhook endpoint remains unauthenticated by user login but still verifies Stripe signatures strictly.
- Secrets are present only in server-side environment variables.
-, no sensitive payloads are written into client-visible logs.
Acceptance criteria I would use:
- 100 percent of expected test webhooks deliver successfully in staging over 10 consecutive attempts
- p95 processing time under 500 ms for synchronous acknowledgment paths
- zero duplicate records across repeated deliveries
- zero silent failures during manual replay tests
Prevention
I would put guardrails around three areas: observability, code review, and release safety.
Monitoring
- Alert on failed webhook deliveries in Stripe Dashboard immediately after launch.
- Add application alerts for non-200 responses on `/api/stripe/webhook`.
- Track count of received events vs processed events daily so gaps show up fast.
- Create an admin view for retryable failures if marketplace operations depend on them.
Code review
For API security lens, I would require reviewers to check:
- authentication boundaries on non-webhook routes
- authorization around order updates and seller payouts
- strict input validation on all received fields
- least privilege for database writes triggered by webhooks
- secret handling with no client exposure
- rate limiting where appropriate on adjacent endpoints
UX guardrails
Silence creates support tickets fast. I would make sure users see:
- clear pending states after checkout
- retry messaging when fulfillment has not completed yet
- support contact path if payment succeeded but activation did not happen within 2 minutes
That reduces refund requests caused by uncertainty rather than actual payment failure.
Performance guardrails
Webhook handlers should be boringly fast:
- keep p95 under 500 ms if doing direct writes only
- move slow side effects into queues when needed
- avoid extra database round trips inside every delivery path
- add indexes on event ID and order reference columns so retries do not become slow under load
When to Use Launch Ready
Launch Ready fits when you need me to fix this without turning it into a long consulting cycle.
I would use it when:
- your MVP already exists but payments or webhooks are unreliable
- you need production deployment repaired fast
- you want monitoring before spending more ad budget
- you need one senior engineer to own launch risk instead of three freelancers guessing at it
What you should prepare:
- repo access with admin permissions
- hosting access such as Vercel or equivalent
- Stripe dashboard access with developer permissions
- current environment variable list
- domain registrar access if DNS changes are needed
- any screenshots of failed checkout flows or support complaints
My recommendation: do not spend another week debugging this piecemeal while users wait on broken fulfillment states. Book Launch Ready if you want me to isolate the failure path first, repair it safely second, and leave you with a deployable handover checklist third.
References
1. https://roadmap.sh/api-security-best-practices 2. https://roadmap.sh/qa 3. https://roadmap.sh/code-review-best-practices 4. https://docs.stripe.com/webhooks 5. 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.