Back to blog
2 min read

Validate Lemon Squeezy Webhook Requests in Cloudflare Workers

To make sure the data sent to your webhook endpoint comes from Lemon Squeezy, you need to verify the signature that comes with the event data.

This signature is found in the X-Signature header. It’s a calculated hash made from a secret key that you set up when creating the webhook through your Lemon Squeezy account, plus the request body.

To confirm that the webhook really comes from Lemon Squeezy, generate a matching hash and compare it with the signature in the header.

This can be done in Cloudflare Workers using the buffer and crypto modules. To use Node.js APIs in your Worker, add the nodejs_compat compatibility flag to your wrangler.toml file:

compatibility_flags = [ "nodejs_compat" ]

After that, you can use the following code to verify the signature:

import { Buffer } from "node:buffer";
import { createHmac } from "node:crypto";

export default {
  async fetch(request, env, ctx) {
    // Get the signature from the request headers
    const signature = request.headers.get("x-signature") ?? ""
    // Get the raw request body
    const body = await request.text();
    // Your secret key
    const secret = "your-secret";
    // Create a hash from the request body using the secret key
    const hmac = createHmac("sha256", secret);
    const digest = Buffer.from(hmac.update(body).digest("hex"), "utf8");
    // Compare the calculated hash with the signature
    const signature = Buffer.from(signature, "utf8");
    const isValid = crypto.subtle.timingSafeEqual(digest, signature);
    if (isValid) {
        return new Response("OK", { status: 200 });
    } else {
        return new Response("Unauthorized", { status: 401 });
    }
  },
};

This code snippet will verify the signature of the webhook request and return a 200 OK response if the signature is valid, or a 401 Unauthorized response if it’s not.