Orders or subscription updates happen, but your app state does not change.
Examples for common stacks. Adapt them to your provider contract and framework.
const crypto = require('crypto');
app.post('/webhooks/lemonsqueezy', express.raw({ type: 'application/json' }), async (req, res) => {
const sig = req.headers['x-signature'];
if (!sig) return res.status(400).send('Missing signature');
const expectedSig = crypto
.createHmac('sha256', process.env.LEMONSQUEEZY_SIGNING_SECRET)
.update(req.body)
.digest('hex');
const received = Buffer.from(sig, 'hex');
const expected = Buffer.from(expectedSig, 'hex');
if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
const eventName = event.meta?.event_name;
const orderId = event.data?.id;
// Idempotency check — store processed event IDs
if (await isProcessed(orderId)) return res.status(200).json({ duplicate: true });
await enqueueLemonEvent(eventName, event);
res.status(200).json({ received: true });
});Provider delivery logs → app ingress logs → signature check → billing state update → replay verification.
Works with webhooks and other async event systems (including AI callbacks). Instead of guessing, inspecting the exact payload and headers can help debug faster.
Try the free webhook testerWas this page helpful?
Your feedback helps us improve the docs.