Stripe Webhook Signature Verification & Event Dispatcher
plain (javascript)
13 hours ago
·
51 lines
·
6 views
1'use strict';
3const crypto = require('crypto');
5/**
6 * Securely verify Stripe webhook signature without external heavy dependencies
7 * @param {string|Buffer} payload - Raw HTTP request body buffer
8 * @param {string} sigHeader - 'stripe-signature' HTTP header
9 * @param {string} secret - Webhook endpoint signing secret (whsec_...)
10 * @param {number} toleranceSec - Signature freshness tolerance in seconds
11 */
12function verifyStripeWebhookSignature(payload, sigHeader, secret, toleranceSec = 300) {
13 if (!sigHeader || !secret) {
14 throw new Error('Missing signature header or signing secret');
15 }
17 const parts = sigHeader.split(',').reduce((acc, item) => {
18 const [k, v] = item.trim().split('=');
19 if (k === 't') acc.timestamp = parseInt(v, 10);
20 if (k === 'v1') acc.signatures.push(v);
21 return acc;
22 }, { timestamp: 0, signatures: [] });
24 const now = Math.floor(Date.now() / 1000);
25 if (Math.abs(now - parts.timestamp) > toleranceSec) {
26 throw new Error('Webhook timestamp exceeds tolerance');
27 }
29 const signedPayload = `${parts.timestamp}.${payload.toString('utf8')}`;
30 const expectedSignature = crypto
31 .createHmac('sha256', secret)
32 .update(signedPayload, 'utf8')
33 .digest('hex');
35 const isValid = parts.signatures.some(sig => {
36 try {
37 return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expectedSignature, 'hex'));
38 } catch {
39 return false;
40 }
41 });
43 if (!isValid) {
44 throw new Error('Invalid webhook signature');
45 }
47 return JSON.parse(payload.toString('utf8'));
48}
50module.exports = { verifyStripeWebhookSignature };
Replies 0
No replies yet
Every reply is a note. Start a discussion, ask a question, or attach a code snippet.
Notification