Чтобы протестировать вебхук WhatsApp Cloud API на локальном хосте, откройте локальную конечную точку через HTTPS, реализуйте проверку GET Meta, а затем сверяйте каждый запрос POST X-Hub-Signature-256 с необработанным телом. Зарегистрируйте URL-адрес туннеля в своем мета-приложении, подпишитесь на учетную запись WhatsApp Business на messages и отправьте тестовое сообщение, чтобы получить реальную полезную нагрузку без развертывания.
Вебхуки WhatsApp используют два разных потока проверки
Самое важное различие заключается в том, что настройка вебхука и доставка вебхука аутентифицируются по-разному. Во время установки Meta отправляет запрос GET, содержащий hub.mode, hub.verify_token и hub.challenge. Ваша конечная точка сравнивает токен проверки и возвращает запрос в виде обычного текста. Позже доставка событий осуществляется через POST запросы; они должны быть аутентифицированы путем проверки подписи HMAC, созданной с помощью секрета вашего мета-приложения.
Токен проверки — это случайная строка, которую вы выбираете; это не токен доступа WhatsApp и не секрет приложения. Возврат вызова доказывает контроль над конечной точкой обратного вызова. Он не аутентифицирует будущие запросы POST. Meta's официальное руководство по вебхуку WhatsApp охватывает настройки обратного вызова, подписки и поля вебхука.
Создание конечной точки маршрутизатора приложений Next.js
Приведенный ниже маршрут обрабатывает обе фазы. Чтение данных POST с помощью request.text() сохраняет точные байты, необходимые для проверки подписи.
// 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 });
}
Не вызывайте request.json(), а затем восстанавливайте JSON для HMAC. Пробелы, экранирование или порядок ключей могут измениться, что приведет к созданию другого дайджеста. Если вы используете Express, захватите Buffer перед глобальным анализатором JSON. Общее руководство по подписи вебхука объясняет обработку необработанного тела в разных платформах.
Запустите туннель и настройте обратный вызов
- Запустите приложение Next.js локально, обычно с помощью
npm run devна порту 3000. - Запустите
npx portpreview 3000в отдельном терминале. - Задайте для
META_VERIFY_TOKENслучайное значение и дляMETA_APP_SECRETзначение App Secret из настроек приложения Meta. - На панели управления мета-разработчиком откройте конфигурацию продукта WhatsApp. страница.
- Установите URL-адрес обратного вызова
https://YOUR-TUNNEL.portpreview.dev/api/webhooks/whatsappи введите тот же токен подтверждения. - После успешной проверки подпишитесь на поле
messagesдля учетной записи WhatsApp Business.
Туннель должен оставаться активным как во время запроса GET, так и во время последующих доставок POST. URL-адрес, скопированный из более старого сеанса, может быть разрешен, но больше не пересылается на ваш компьютер, поэтому подтверждайте точный обратный вызов при каждом изменении локального туннеля.
Протестируйте запрос GET независимо
Перед использованием панели мониторинга воспроизведите запрос локально:
curl -i \
"http://localhost:3000/api/webhooks/whatsapp?hub.mode=subscribe&hub.verify_token=${META_VERIFY_TOKEN}&hub.challenge=123456"
Правильный ответ – статус 200 с телом 123456, а не JSON и не "123456" с кавычками. Если токен неправильный, подойдет 403. Не регистрируйте параметры запроса, поскольку там появляется токен проверки.
Определите полезную нагрузку сообщений, прежде чем писать бизнес-логику.
WhatsApp упаковывает данные на несколько уровней. Типичное уведомление содержит object: "whatsapp_business_account", массив entry, массив changes и изменение, field которого равно messages. Внутри value входящий пользовательский контент отображается в messages; Обновления о доставке, прочтении и сбоях для отправленных вами сообщений отображаются в 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);
}
}
}
Не думайте, что каждое уведомление содержит текстовое сообщение. Изображения, аудио, документы, местоположения, интерактивные ответы, системные сообщения и полезные данные, предназначенные только для статуса, имеют разные формы. Держите диспетчер с ключом message.type, проверяйте необязательные поля и сохраняйте неизвестные типы событий для проверки, чтобы избежать сбоя.
Проверьте правильность подписи POST
Значение X-Hub-Signature-256 использует форму sha256=<hex digest>. Вычислите HMAC-SHA256 по необработанным байтам запроса, используя Мета-секрет приложения. Постоянный или временный токен доступа WhatsApp используется для вызовов API Graph; это не ключ HMAC. Используйте сравнение в постоянное время и отклоняйте отсутствующую подпись.
Оставьте включенной локальную проверку. Любой, кто узнает URL-адрес туннеля, может отправить к нему произвольный JSON. Без проверки поддельное событие может вызвать автоматические ответы, изменить записи CRM или раскрыть состояние клиента. Поменяйте секрет приложения, если он случайно зафиксирован, распечатан или передан.
Быстрое подтверждение и дедупликация сообщений
Верните 200 после аутентификации и постоянной постановки события в очередь. Не ждите загрузки мультимедиа, звонка в LLM или обновления нескольких услуг. Поставщики повторяют доставку, когда подтверждения не удается, а неоднозначность сети означает, что дубликаты являются нормальным явлением.
Используйте сообщение WhatsApp id в качестве ключа идемпотентности для входящих сообщений и объектов состояния. Установите уникальное ограничение для обрабатываемых идентификаторов. Переходы статуса могут законно переходить от отправки к доставке и чтению, поэтому дедупликация каждого соответствующего перехода не отбрасывает более позднее состояние.
Устранение неполадок с настройкой вебхука WhatsApp
URL обратного вызова не удалось проверить
Протестируйте маршрут GET через общедоступный URL-адрес. Убедитесь, что он принимает GET, сравнивает точный токен проверки и отвечает только запросом. Перенаправления, промежуточное программное обеспечение аутентификации, перезапись локали или оболочка JSON могут нарушить проверку. Убедитесь, что переменная среды загружена запущенным процессом разработки.
Проверка прошла успешно, но сообщений не поступает.
Проверка обратного вызова сама по себе не подписывается учетной записью WhatsApp Business на поля. Подтвердите подписку messages на панели управления и убедитесь, что номер телефона принадлежит ожидаемому приложению и учетной записи. Отправьте сообщение от разрешенного получателя, если приложение все еще находится в режиме разработки.
Каждый POST не проходит проверку подписи
Обычными причинами являются использование токена доступа вместо App Secret, хеширование проанализированного JSON, отсутствие префикса sha256= или сравнение различных кодировок. Длина тела журнала и наличие заголовка, но никогда не печатайте секретные или полные полезные данные клиента.
Текстовые сообщения работают, но обработка мультимедиа невозможна.
Медиа-уведомления содержат идентификатор, а не обязательно байты файла. Получите медиафайлы через Graph API с действительным токеном доступа, а затем загрузите их. Держите этот более медленный рабочий процесс за пределами пути подтверждения вебхука.
Локальная конечная точка видит повторяющиеся события.
Проверяйте состояние ответа и задержку, добавляйте устойчивую идемпотентность и воспроизводите одно захваченное событие после каждого исправления. В руководстве по воспроизведению вебхука показано, как избежать отправки нового реального сообщения при каждом изменении кода.
Защитите данные клиентов во время локальных тестов.
- Используйте тестовые номера телефонов и синтетические разговоры, где это возможно.
- Редактируйте номера телефонов, тела сообщений, URL-адреса мультимедиа, контакты, и имена профилей из журналов.
- Храните секрет приложения, токены доступа и проверяйте токены только в игнорируемых файлах среды или в диспетчере секретов.
- Ограничьте круг лиц, которые могут просматривать снимки туннеля, и удалите их после сеанса отладки.
- Проверяйте идентификаторы объектов, полей и учетных записей перед выполнением бизнес-действий.
Туннель создает итерация выполняется быстро, но при этом также переносится персональные данные, имеющие производственную форму, на машину разработчика. Примените элементы управления из контрольного списка безопасности туннеля перед тестированием на реальных пользователях.
