Para probar un webhook de la API de WhatsApp Cloud en localhost, exponga su punto final local a través de HTTPS, implemente el desafío de verificación GET de Meta y luego verifique cada solicitud POST X-Hub-Signature-256 con el cuerpo sin formato. Registre el túnel URL en su metaaplicación, suscríbase a la cuenta empresarial de WhatsApp en messages y envíe un mensaje de prueba para recibir una carga útil real sin implementarla.
Los webhooks de WhatsApp utilizan dos flujos de verificación diferentes
La distinción más importante es que la configuración y la entrega del webhook se autentican de manera diferente. Durante la configuración, Meta envía una solicitud GET que contiene hub.mode, hub.verify_token y hub.challenge. Su punto final compara el token de verificación y devuelve el desafío como texto sin formato. Posteriormente, las entregas de eventos son solicitudes POST; estos deben autenticarse validando la firma HMAC creada con su Meta App Secret.
Un token de verificación es una cadena aleatoria que usted elige; No es el token de acceso de WhatsApp ni el secreto de la aplicación. Devolver el desafío demuestra el control del punto final de devolución de llamada. No autentica futuras solicitudes POST. guía oficial de webhook de WhatsApp de Meta cubre la configuración de devolución de llamada, suscripciones y campos de webhook.
Crear un punto final de Next.js App Router
La ruta siguiente maneja ambas fases. La lectura de datos POST con request.text() conserva los bytes exactos necesarios para la verificación de la firma.
// app/api/webhooks/whatsapp/route.ts
import crypto from 'node:crypto';
import { NextRequest } from 'next/server';
export const runtime = 'nodejs';
export async function GET(request: NextRequest) {
const mode = request.nextUrl.searchParams.get('hub.mode');
const token = request.nextUrl.searchParams.get('hub.verify_token');
const challenge = request.nextUrl.searchParams.get('hub.challenge');
if (mode === 'subscribe' && token === process.env.META_VERIFY_TOKEN) {
return new Response(challenge ?? '', { status: 200 });
}
return new Response('Forbidden', { status: 403 });
}
export async function POST(request: Request) {
const rawBody = await request.text();
const supplied = request.headers.get('x-hub-signature-256') ?? '';
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.META_APP_SECRET!)
.update(rawBody)
.digest('hex');
const a = Buffer.from(supplied);
const b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return new Response('Invalid signature', { status: 401 });
}
const payload = JSON.parse(rawBody);
await enqueueWhatsAppPayload(payload);
return new Response('EVENT_RECEIVED', { status: 200 });
}
No llame a request.json() y luego reconstruya el JSON para HMAC. Los espacios en blanco, el escape o el orden de las claves pueden cambiar, produciendo un resumen diferente. Si usa Express, capture un Buffer antes de un analizador JSON global. La guía general de firma de webhook explica el manejo del cuerpo sin formato en los marcos.
Inicie un túnel y configure la devolución de llamada
- Ejecute la aplicación Next.js localmente, normalmente con
npm run deven el puerto 3000. - Ejecute
npx portpreview 3000en una terminal separada. - Establezca
META_VERIFY_TOKENen un valor aleatorio yMETA_APP_SECRETen el secreto de la aplicación desde la configuración de la aplicación Meta. - En el panel de desarrollador de Meta, abra la configuración del producto WhatsApp. página.
- Establezca la URL de devolución de llamada en
https://YOUR-TUNNEL.portpreview.dev/api/webhooks/whatsappe ingrese el mismo token de verificación. - Después de que la verificación sea exitosa, suscríbase al campo
messagesde la cuenta comercial de WhatsApp.
El túnel debe permanecer activo durante el desafío GET y las entregas POST posteriores. Una URL copiada de una sesión anterior puede resolverse pero ya no reenviarse a su máquina, así que confirme la devolución de llamada exacta cada vez que cambie el túnel local.
Pruebe el desafío GET de forma independiente
Antes de usar el panel, reproduzca la solicitud localmente:
curl -i \
"http://localhost:3000/api/webhooks/whatsapp?hub.mode=subscribe&hub.verify_token=${META_VERIFY_TOKEN}&hub.challenge=123456"
Una respuesta correcta es el estado 200 con cuerpo 123456, ni JSON ni "123456" entre comillas. Si el token es incorrecto, 403 es apropiado. No registre los parámetros de consulta porque el token de verificación aparece allí.
Comprenda la carga útil de un mensaje antes de escribir la lógica empresarial
WhatsApp encapsula los datos en varios niveles de profundidad. Una notificación típica tiene object: "whatsapp_business_account", una matriz entry, una matriz changes y un cambio cuyo field es messages. Dentro de value, el contenido del usuario entrante aparece en messages; Las actualizaciones de entrega, lectura y fallas de los mensajes que envió aparecen en statuses.
for (const entry of payload.entry ?? []) {
for (const change of entry.changes ?? []) {
if (change.field !== 'messages') continue;
for (const message of change.value.messages ?? []) {
await handleInboundMessage({
id: message.id,
from: message.from,
type: message.type,
text: message.text?.body,
});
}
for (const status of change.value.statuses ?? []) {
await updateDeliveryStatus(status.id, status.status);
}
}
}
No asuma que cada notificación contiene un mensaje de texto. Las imágenes, el audio, los documentos, las ubicaciones, las respuestas interactivas, los mensajes del sistema y las cargas útiles de solo estado tienen diferentes formas. Mantenga un despachador con la clave message.type, valide los campos opcionales y conserve los tipos de eventos desconocidos para su revisión en lugar de fallar.
Verifique la firma POST correctamente
El valor X-Hub-Signature-256 usa el formulario sha256=<hex digest>. Calcule HMAC-SHA256 sobre los bytes de solicitud sin procesar utilizando el Meta App Secret. El token de acceso permanente o temporal de WhatsApp se utiliza para las llamadas a Graph API; no es la clave HMAC. Utilice la comparación de tiempo constante y rechace una firma faltante.
Mantenga habilitada la verificación local. Cualquiera que conozca la URL de un túnel puede PUBLICAR JSON arbitrario en él. Sin verificación, un evento falsificado podría desencadenar respuestas automáticas, modificar registros de CRM o exponer el estado del cliente. Gire el secreto de la aplicación si se confirma, se imprime o se comparte accidentalmente.
Reconozca rápidamente y elimine mensajes duplicados
Devuelva 200 después de autenticar y poner en cola de forma duradera el evento. No espere mientras descarga medios, llama a un LLM o actualiza varios servicios. Los proveedores vuelven a intentar las entregas cuando fallan los acuses de recibo y la ambigüedad de la red significa que los duplicados son normales.
Utilice el mensaje de WhatsApp id como clave de idempotencia para mensajes entrantes y objetos de estado. Establezca una restricción única en torno a las identificaciones procesadas. Las transiciones de estado pueden progresar legítimamente desde enviado hasta entregado y leído, por lo tanto, deduplica cada transición relevante sin descartar un estado posterior.
Solucionar problemas de configuración del webhook de WhatsApp
No se pudo validar la URL de devolución de llamada
Prueba la ruta GET a través de la URL pública. Asegúrese de que acepte GET, compare el token de verificación exacto y responda solo con el desafío. Los redireccionamientos, el middleware de autenticación, las reescrituras locales o un contenedor JSON pueden interrumpir la verificación. Confirme que la variable de entorno sea cargada por el proceso de desarrollo en ejecución.
La verificación se realiza correctamente pero no llega ningún mensaje
La verificación de devolución de llamada por sí sola no suscribe la cuenta empresarial de WhatsApp a los campos. Confirme la suscripción messages en el panel y que el número de teléfono pertenezca a la aplicación y cuenta esperadas. Envía un mensaje desde un destinatario permitido si la aplicación aún está en modo de desarrollo.
Cada POST no supera la validación de firma
Las causas habituales son el uso del token de acceso en lugar del secreto de la aplicación, el JSON analizado mediante hash, la omisión del prefijo sha256= o la comparación de diferentes codificaciones. Registre la longitud del cuerpo y si existe el encabezado, pero nunca imprima la carga útil secreta o completa del cliente.
Los mensajes de texto funcionan pero el manejo de medios falla
Las notificaciones de medios contienen una identificación, no necesariamente los bytes del archivo. Obtenga medios a través de Graph API con un token de acceso válido y luego descárguelo. Mantenga ese flujo de trabajo más lento fuera de la ruta de confirmación del webhook.
El punto final local ve eventos duplicados
Inspeccione el estado de respuesta y la latencia, agregue idempotencia duradera y reproduzca un evento capturado después de cada corrección. La guía de reproducción de webhook muestra cómo evitar enviar un nuevo mensaje real por cada cambio de código.
Proteja los datos de los clientes durante las pruebas locales
- Utilice números de teléfono de prueba y conversaciones sintéticas siempre que sea posible.
- Redacte números de teléfono, cuerpos de mensajes, URL de medios, contactos y nombres de perfiles de los registros.
- Almacenar aplicaciones secretas, tokens de acceso y verificar tokens solo en archivos de entorno ignorados o en un administrador secreto.
- Restringir quién puede ver las capturas del túnel y eliminarlas después de la sesión de depuración.
- Validar los identificadores de objetos, campos y cuentas antes de ejecutar acciones comerciales.
Se crea un túnel La iteración es rápida, pero también lleva datos personales en forma de producción a una máquina de desarrollo. Aplique los controles de la lista de verificación de seguridad del túnel antes de realizar pruebas con usuarios reales.
