webhookslisted
Install: claude install-skill claude-dev-suite/claude-dev-suite
# Webhooks
## Receiving Webhooks
### Express (with signature verification)
```typescript
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['stripe-signature']!;
const event = stripe.webhooks.constructEvent(req.body, signature, WEBHOOK_SECRET);
// Idempotency: check if already processed
const existing = await db.webhookEvent.findUnique({ where: { eventId: event.id } });
if (existing) return res.json({ received: true });
// Process
switch (event.type) {
case 'checkout.session.completed':
await fulfillOrder(event.data.object);
break;
}
// Mark as processed
await db.webhookEvent.create({ data: { eventId: event.id, type: event.type } });
res.json({ received: true });
});
```
### Generic HMAC Verification
```typescript
import { createHmac, timingSafeEqual } from 'crypto';
function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret).update(payload).digest('hex');
return timingSafeEqual(Buffer.from(signature), Buffer.from(`sha256=${expected}`));
}
app.post('/webhooks/github', express.raw({ type: '*/*' }), (req, res) => {
const sig = req.headers['x-hub-signature-256'] as string;
if (!verifyWebhookSignature(req.body.toString(), sig, GITHUB_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process event...
res.status(200).json({ received: true });
})