Verify webhook signatures
Check that that received events are genuinely sent from Paddle by verifying webhook signatures.
For security, verify webhook signatures to make sure received webhooks are genuinely sent from Paddle. This helps you be sure they haven't been tampered with in-transit.
How it works
Webhooks let you subscribe to events in Paddle. When a subscribed event occurs, Paddle sends a notification to a webhook endpoint that you specify.
All webhooks sent by Paddle include a Paddle-Signature
header. Paddle generates this header using a secret key that only you and Paddle know.
To verify, you can use the secret key to generate your own signature for each webhook. Since only you and Paddle know the secret key, if both signatures match then you can be sure that a received event came from Paddle.
Before you begin
Create a notification destination
Create a notification destination where the type is url
(webhook), if you haven't already.
Get your secret key
To verify webhooks, you'll need to get the secret key for your notification destination.
Paddle generates a secret key for each notification destination that you create. If you've created more than one notification destination, get keys for each notification destination that you want to verify signatures for.
Treat your endpoint secret key like a password. Keep it safe and never share it with apps or people you don't trust.
Send a GET request to the /notification-settings/{notification_setting_id}
endpoint.
Paddle ID of the notification entity to work with.
Response
If successful, Paddle returns the notification destination settings, including the endpoint_secret_key
. For example:
12131415161718192021222324252612 {
13 "name": "subscription.past_due",
14 "description": "Occurs when a subscription is past due.",
15 "group": "Subscription",
16 "available_versions": [
17 1
18 ]
19 }
20 ],
21 "endpoint_secret_key": "pdl_ntfset_01gkpjp8bkm3tm53kdgkx6sms7_a7e4a56cf7c1cd80cb1c735c72bab2aa75d06fe08b8c50a18cf6afbcfe834122"
22 },
23 "meta": {
24 "request_id": "968bf13b-ca51-4a9c-934a-1760b298620d"
25 }
26}
Get started
There are two ways that you can verify webhook signatures:
Verify using Paddle SDKs (recommended)
Use helper functions or classes in our official SDKs to verify webhook signatures.
Build your own logic to verify webhook signatures.
Verify using Paddle SDKs
Use our official SDKs to verify webhook signatures. You'll need to provide the event payload, Paddle-Signature
header, and the endpoint secret key.
Don't transform or process the raw body of the request, including adding whitespace or applying other formatting. This results in a different signed payload, meaning signatures won't match when you compare.
You can use a middleware to verify the signature of an incoming request before processing it.
123456789101112131verifier := paddle.NewWebhookVerifier(os.Getenv("WEBHOOK_SECRET_KEY"))
2// Wrap your handler with the verifier.Middleware method
3handler := verifier.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
4 // The request making it this far means the webhook was verified
5 // Best practice here is to check if you have processed this webhook already using the event id
6 // At this point you should store for async processing
7 // For example a local queue or db entry
8
9 // Respond as soon as possible with a 200 OK
10 w.Header().Set("Content-Type", "application/json")
11 w.WriteHeader(http.StatusOK)
12 w.Write([]byte(`{"success": true}`))
13}))
You can also verify the signature of an incoming request manually.
1231webhookVerifier := paddle.NewWebhookVerifier(os.Getenv("WEBHOOK_SECRET_KEY"))
2// Note: the request (req *http.Request) should be pass exactly as it comes without altering it.
3ok, err := webhookVerifier.Verify(req)
Learn more about the Paddle Go SDK on GitHub: @PaddleHQ/paddle-go-sdk.
Verify manually
Build your own logic to verify webhook signatures from Paddle in five steps:
Get the
Paddle-Signature
header from an incoming webhook sent by Paddle.Extract timestamp and signature from the header
Parse the header to extract its timestamp and signature values.
Concatenate the extracted timestamp with the raw body of the request to build a signed payload.
Hash the signed payload you built to generate a signature that you can use for comparison.
Compare the
Paddle-Signature
header to the signature you just computed.
1. Get Paddle-Signature header
First, get the Paddle-Signature
header from an incoming webhook sent by Paddle.
All webhook events sent by Paddle include a Paddle-Signature
header. For example:
11ts=1671552777;h1=eb4d0dc8853be92b7f063b9f3ba5233eb920a09459b6e6b2c26705b4364db151
Signatures include two parts, separated by a semicolon:
Timestamp as a Unix timestamp.
Webhook event signature. Signatures contain at least one h1
. We may add support for secret rotation in the future. During secret rotation, more than one h1
is returned while secrets are rotated out.
2. Extract timestamp and signature from header
Now you have the Paddle-Signature
header, parse it to extract the timestamp (ts
) and signature values (h1
).
You can do this by splitting using a semicolon character (;
) to get elements, then splitting again using an equals sign character (=
) to get key-value pairs.
To prevent replay attacks, you may like to check the timestamp (
ts
) against the current time and reject events that are too old. Our SDKs have a default tolerance of five seconds between the timestamp and the current time.
3. Build signed payload
Paddle creates a signature by first concatenating the timestamp (ts
) with the body of the request, joined with a colon (:
).
Build your own signed payload by concatenating:
- The extracted timestamp (
ts
) + - A colon (
:
) + - The raw body of the request
Don't transform or process the raw body of the request, including adding whitespace or applying other formatting. This results in a different signed payload, meaning signatures won't match when you compare.
4. Hash signed payload
Next, hash your signed payload to generate a signature.
Paddle generates signatures using a keyed-hash message authentication code (HMAC) with SHA256 and a secret key.
Compute the HMAC of your signed payload using the SHA256 algorithm, using the secret key for this notification destination as the key.
This should give you the expected signature of the webhook event.
5. Compare signatures
Finally, compare the Paddle-Signature
header to the signature you just computed.
If they don't match, you should reject the webhook event. Someone may be sending malicious requests to your webhook endpoint.
Related pages
- Verify webhook signatures
- How it works
- Before you begin
- Create a notification destination
- Get your secret key
- Get started
- Verify using Paddle SDKs
- Verify manually
- 1. Get Paddle-Signature header
- 2. Extract timestamp and signature from header
- 3. Build signed payload
- 4. Hash signed payload
- 5. Compare signatures
- Related pages