// Express.js
import express from 'express';
import crypto from 'crypto';
const app = express();
function verifyWebhookSignature(payload: string, signatureHeader: string, secret: string): boolean {
const parts: Record<string, string> = {};
for (const pair of signatureHeader.split(',')) {
const [key, value] = pair.split('=');
parts[key] = value;
}
const timestamp = parseInt(parts.t, 10);
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false; // replay protection
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${payload}`)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
app.post(
'/webhooks/pictify',
express.raw({ type: 'application/json' }),
async (req, res) => {
// Verify signature
const signature = req.headers['x-pictify-signature'] as string;
const isValid = verifyWebhookSignature(
req.body.toString(),
signature,
process.env.PICTIFY_WEBHOOK_SECRET!
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Parse and handle event
const event = JSON.parse(req.body.toString());
await handleEvent(event);
res.status(200).send('OK');
}
);
async function handleEvent(event: WebhookEvent) {
switch (event.event) {
case 'render.completed':
await onRenderCompleted(event.data);
break;
case 'render.failed':
await onRenderFailed(event.data);
break;
case 'batch.completed':
await onBatchCompleted(event.data);
break;
case 'binding.updated':
await onBindingUpdated(event.data);
break;
}
}