@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
JavaScript
;
/**
* @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;
}