Paddle webhook signature verification in Next.js
Next.js teams often break Paddle webhook verification when a route parses JSON before checking the exact raw body behind the Paddle-Signature header.
Generate a test planRead the raw body first
In an App Router webhook route, read await request.text() before calling JSON.parse or request.json(). The signature check must use the exact string Paddle signed.
- Valid signed payload returns 2xx.
- Changed whitespace, key order, or line endings return 400 before side effects.
- Wrong
Paddle-Signatureheader returns 400 before account access changes.
Keep signed Paddle fixtures
Store fake transaction.completed, subscription lifecycle, cancellation, and adjustment payloads with test Paddle-Signature headers so CI protects the raw-body behavior.
Replay duplicates safely
Run the same signed event multiple times and assert that entitlements, downloads, invoices, and emails happen once. Signature verification proves authenticity; idempotency proves retry safety.
Separate provider verification
Verify Paddle before normalizing the event into your internal billing model, just as you would keep Stripe, Lemon Squeezy, and Polar verification separate. Do not reuse a Stripe signing helper for Paddle without checking header format and raw-body rules.
FAQ
Why does Paddle webhook signature verification fail in Next.js?
The common cause is parsing or changing the request body before verification. The Paddle-Signature check must use the exact raw body delivered to the route.
Can Paddle signature tests run without a live Paddle account?
Yes. Route tests can use fake JSON fixtures and fake signing secrets. A separate checkout smoke test is still required before production traffic.
Use the free sample before wiring production checkout
Download a public sample pack with fake fixtures, signature tests, contract checks, duplicate replay tests, and CI skeletons. The Pro Kit preview shows the full paid package structure without exposing the private archive.