UNPKG

@jackiemacklein/nettz-utils

Version:

Serviços de imagem, e-mail, códigos de barras, utilitários numéricos e componentes React para apps Node.js com TypeScript

72 lines (71 loc) 2.76 kB
"use strict"; /** * @author Jackiê Macklein * @company Onside tecnologia/Nettz * @copyright Todos direitos reservados. * @description Utilitários para webhooks da WhatsApp Business Platform (Meta). * Referência: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview */ Object.defineProperty(exports, "__esModule", { value: true }); exports.getWhatsAppWebhookChallenge = getWhatsAppWebhookChallenge; exports.verifyWhatsAppWebhookSignature = verifyWhatsAppWebhookSignature; exports.parseWhatsAppWebhookPayload = parseWhatsAppWebhookPayload; const crypto_1 = require("crypto"); function firstQueryValue(query, key) { const v = query[key]; if (v === undefined || v === null) return undefined; if (Array.isArray(v)) return v[0]; return v; } /** * Verificação inicial do webhook (GET): Meta envia `hub.mode`, `hub.verify_token`, `hub.challenge`. * Se o token confere, devolva o `hub.challenge` no corpo da resposta com status 200. * * @returns string a ser retornada como texto/resposta, ou `null` se não verificar. */ function getWhatsAppWebhookChallenge(query, expectedVerifyToken) { const mode = firstQueryValue(query, "hub.mode"); const token = firstQueryValue(query, "hub.verify_token"); const challenge = firstQueryValue(query, "hub.challenge"); if (mode === "subscribe" && token === expectedVerifyToken && challenge) { return challenge; } return null; } const SIG_PREFIX = "sha256="; /** * Valida o header `X-Hub-Signature-256` do POST (corpo **bruto** UTF-8, sem re-serializar o JSON). * Usa o **App Secret** do app Meta. */ function verifyWhatsAppWebhookSignature(rawBody, signatureHeader, appSecret) { if (!signatureHeader || !signatureHeader.startsWith(SIG_PREFIX)) { return false; } const receivedHex = signatureHeader.slice(SIG_PREFIX.length).trim(); const expectedHex = (0, crypto_1.createHmac)("sha256", appSecret) .update(rawBody, "utf8") .digest("hex"); try { return (0, crypto_1.timingSafeEqual)(Buffer.from(receivedHex, "hex"), Buffer.from(expectedHex, "hex")); } catch { return false; } } /** * Faz `JSON.parse` e valida estrutura mínima `object` + `entry` (array). * Não valida assinatura; use `verifyWhatsAppWebhookSignature` antes, com o body bruto. */ function parseWhatsAppWebhookPayload(rawBody) { const data = JSON.parse(rawBody); if (!data || typeof data !== "object") { throw new TypeError("WhatsApp webhook: JSON inválido"); } const o = data; if (typeof o.object !== "string" || !Array.isArray(o.entry)) { throw new TypeError("WhatsApp webhook: formato de envelope inesperado"); } return data; }