// Receptor de webhooks do motor NewWhats (motor → satélite). // POST /api/webhook/newwhats // Header x-nw-signature: sha256=HMAC-SHA256(webhook_secret, rawBody) // Eventos: message.new (nova mensagem WA), session.status (conexão da instância) // // Versão ENXUTA p/ scoreodonto: valida HMAC, registra o evento e deixa ganchos // (TODO) para captura de lead / auto-resposta. NÃO porta o auto-responder do // mercado (a Secretária do motor já responde via /secretaria/ask). import { Router } from 'express' import { createHmac, timingSafeEqual } from 'crypto' import { getConfig } from './config.js' function verifySignature(secret, rawBody, sigHeader) { if (!sigHeader) return false const expected = Buffer.from('sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex')) const received = Buffer.from(String(sigHeader)) return expected.length === received.length && timingSafeEqual(expected, received) } export function createWebhookReceiver(_pool) { const router = Router() router.post('/api/webhook/newwhats', async (req, res) => { try { const secret = getConfig().webhookSecret if (secret) { const rawBody = req.rawBody ?? Buffer.from(JSON.stringify(req.body)) if (!verifySignature(secret, rawBody, req.headers['x-nw-signature'])) { return res.status(401).json({ error: 'Assinatura inválida' }) } } else { console.warn('[nw-webhook] NEWWHATS_WEBHOOK_SECRET ausente — assinatura ignorada') } const { event, data } = req.body || {} if (event === 'message.new') { // TODO(odonto): registrar lead / acionar fluxo. Hoje só loga. console.log(`[nw-webhook] message.new de ${data?.author ?? data?.pushName ?? '?'}: ${String(data?.body ?? '').slice(0, 60)}`) } else if (event === 'session.status') { console.log(`[nw-webhook] session.status → ${data?.instanceId}: ${data?.status}`) } else { console.log(`[nw-webhook] evento não tratado: ${event}`) } res.json({ ok: true }) } catch (e) { console.error('[nw-webhook] erro:', e.message) res.status(500).json({ error: e.message }) } }) return router }