Back to Business Mode

Webhook Security

When a payment provider notifies your app that "this order was paid," your server has to answer one question before acting on it: did this request actually come from the payment provider, or did someone just POST a convincing-looking JSON body to a guessable URL?

A webhook URL is public by nature — anything reachable at all is reachable by an attacker who finds or guesses it
Signature verification (HMAC over the raw request body, using a shared secret) proves the sender knew the secret, which a forged request can't fake
The raw, unparsed request body must be used for signature verification — re-serializing parsed JSON can produce different bytes and silently break the check
Replay protection (a timestamp checked against a tolerance window, plus tracking already-seen event IDs) stops a captured, valid webhook from being re-sent later

Hands-on demo

Forge a payment webhook

You don't know the real signing secret — try sending a fabricated "payment succeeded" event anyway and see if the endpoint falls for it.

Try it yourself

Try it yourself — run a scenario below, then flip this and replay it to see what changes.

Legitimate Payment Confirmation

The real payment provider notifies the app that an order was paid.

Want this flow explained in detail as it plays?

app.flowground.dev/webhooks

Incoming webhook

Eventorder.paid
Orderorder_1042
Payment Provider
App
"order_1042 was paid", signed with a secret only the app and the provider share.

Selected hop

Payment provider POSTs an event to /webhooks/payments

Payment Provider

The external service that notifies the app when a payment event happens.

App

The application's backend — enforces business rules and decides what every request is allowed to do.

What happens here

"order_1042 was paid", signed with a secret only the app and the provider share.