Most Paddle webhook signature failures come down to one thing: you verified the signature against the wrong bytes. Your framework parsed the JSON, you re-serialized it, and the HMAC no longer matches what Paddle signed. The event looks tampered with even though nothing is wrong. This post shows the exact verification Paddle Billing expects, the raw-body trap that breaks it, and the difference between Paddle Billing and the older Paddle Classic scheme that sends people down the wrong path.
EventDock's own billing runs on Paddle Billing, so the code below is the same shape we use in production.
First: are you on Paddle Billing or Paddle Classic?
Paddle has two products with two completely different webhook signature schemes, and a lot of the older blog posts and Stack Overflow answers describe the wrong one.
Paddle Classic (the original product) sends a p_signature field inside the form-encoded body. It requires PHP serialization and is verified with Paddle's public key.






