Exemplo de Receptor
Exemplo de receptor (Node.js + Express)
Implementação completa de um receptor de webhooks da GDREdu em Node.js com Express. Inclui validação de assinatura, idempotência e fila local para processamento assíncrono.
Código completo
// webhook-receiver.js
import express from 'express'
import crypto from 'crypto'
const app = express()
// IMPORTANTE: Use raw body para validar a assinatura corretamente
app.use(express.json({
limit: '1mb',
verify: (req, res, buf) => {
req.rawBody = buf.toString('utf8')
},
}))
// ============================================
// Armazenamento de webhooks e deliveries
// ============================================
// Substitua por um armazenamento seguro de sua preferência
const WEBHOOKS = new Map() // webhookId → secret
const PROCESSED = new Set() // idempotência
// Configure seus webhooks manualmente ou via endpoint admin
WEBHOOKS.set('default-secret-id', process.env.WEBHOOK_SECRET || 'whsec_coloque_seu_secret_aqui')
// ============================================
// Fila local (substitua por uma fila persistente)
// ============================================
const QUEUE = []
function enqueue (deliveryId, event) {
QUEUE.push({ deliveryId, event })
processQueue()
}
async function processQueue () {
while (QUEUE.length > 0) {
const { deliveryId, event } = QUEUE.shift()
try {
await processEvent(deliveryId, event)
} catch (err) {
console.error(`[worker] erro ao processar ${deliveryId}:`, err)
}
}
}
// ============================================
// Processamento de evento
// ============================================
async function processEvent (deliveryId, event) {
// Idempotência: pular se já processamos esta delivery
if (PROCESSED.has(deliveryId)) {
console.log(`[skip] ${deliveryId} já processado`)
return
}
PROCESSED.add(deliveryId)
const { type, data } = event
console.log(`[event] ${type} — person: ${data.person.name} (${data.person.type})`)
switch (type) {
case 'equipment.recognized':
// Exemplo: salvar no seu banco, enviar push, etc
await saveToYourDatabase(event)
break
default:
console.warn(`[warn] tipo de evento desconhecido: ${type}`)
}
}
async function saveToYourDatabase (event) {
// Substitua pelo seu armazenamento
console.log(`[db] presença salva: ${event.data.person.name}`)
}
// ============================================
// Validação de assinatura
// ============================================
function verifySignature (rawBody, signatureHeader, secret) {
if (!signatureHeader || !signatureHeader.startsWith('sha256=')) return false
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
if (signatureHeader.length !== expected.length) return false
return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected))
}
// ============================================
// Endpoint principal
// ============================================
app.post('/webhooks/gdredu', (req, res) => {
const sig = req.headers['x-gdredu-signature']
const deliveryId = req.headers['x-gdredu-delivery-id']
const event = req.body
// Obter o secret do webhook (busque do seu banco em produção)
const secret = WEBHOOKS.get('default-secret-id')
if (!verifySignature(req.rawBody, sig, secret)) {
console.warn(`[401] assinatura inválida para delivery ${deliveryId}`)
return res.status(401).send('Invalid signature')
}
// Responde 200 imediatamente e processa em background
res.status(200).json({ ok: true })
// Enfileira para processamento assíncrono
enqueue(deliveryId, event)
})
// ============================================
// Health check
// ============================================
app.get('/health', (req, res) => res.json({ ok: true }))
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`[receiver] escutando em http://localhost:${PORT}/webhooks/gdredu`)
})Como executar
-
Configure o secret:
export WEBHOOK_SECRET="whsec_..." # o secret que a API retornou -
Inicie o receptor:
node webhook-receiver.js -
Exponha para a internet (use ngrok, Cloudflare Tunnel, etc):
ngrok http 3000 # Anote a URL HTTPS gerada (ex: https://abc123.ngrok.io) -
Cadastre o webhook na API:
curl -X POST "https://api.gdredu.com/v1/webhooks" \ -H "Authorization: Bearer gdredu_live_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://abc123.ngrok.io/webhooks/gdredu", "events": ["equipment.recognized"] }' -
Teste o webhook:
curl -X POST "https://api.gdredu.com/v1/webhooks/<id>/test" \ -H "Authorization: Bearer gdredu_live_..." \ -H "x-branch-id: branch-id"Você deve ver o evento chegando no log do receptor.
Múltiplos webhooks (múltiplos secrets)
Em produção, diferentes unidades podem ter webhooks diferentes. Mapeie por webhookId (ou por branchId) e armazene o secret no seu banco:
async function getSecretForDelivery (webhookId) {
// buscar do banco
return await db.webhooks.getSecret(webhookId)
}Armazenamento de deliveries
Para auditoria e debug, salve todas as deliveries recebidas:
await db.deliveries.create({
id: deliveryId,
event: event.type,
receivedAt: new Date(),
payload: event,
signatureValid: true,
})Updated about 21 hours ago
Did this page help you?
