Все статьи
События мобильных сообщений проходят проверку вебхука Meta и по защищённому туннелю поступают в приложение на localhost.
WhatsApp Cloud APIMeta webhookslocalhostwebhook security

Тестирование вебхуков WhatsApp Cloud API на localhost

Чтобы протестировать вебхук 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. Общее руководство по подписи вебхука объясняет обработку необработанного тела в разных платформах.

Запустите туннель и настройте обратный вызов

  1. Запустите приложение Next.js локально, обычно с помощью npm run dev на порту 3000.
  2. Запустите npx portpreview 3000 в отдельном терминале.
  3. Задайте для META_VERIFY_TOKEN случайное значение и для META_APP_SECRET значение App Secret из настроек приложения Meta.
  4. На панели управления мета-разработчиком откройте конфигурацию продукта WhatsApp. страница.
  5. Установите URL-адрес обратного вызова https://YOUR-TUNNEL.portpreview.dev/api/webhooks/whatsapp и введите тот же токен подтверждения.
  6. После успешной проверки подпишитесь на поле 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-адреса мультимедиа, контакты, и имена профилей из журналов.
  • Храните секрет приложения, токены доступа и проверяйте токены только в игнорируемых файлах среды или в диспетчере секретов.
  • Ограничьте круг лиц, которые могут просматривать снимки туннеля, и удалите их после сеанса отладки.
  • Проверяйте идентификаторы объектов, полей и учетных записей перед выполнением бизнес-действий.

Туннель создает итерация выполняется быстро, но при этом также переносится персональные данные, имеющие производственную форму, на машину разработчика. Примените элементы управления из контрольного списка безопасности туннеля перед тестированием на реальных пользователях.

Часто задаваемые вопросы

Как протестировать веб-перехватчик WhatsApp Cloud API на локальном хосте?
Запустите обработчик веб-перехватчика локально, откройте его с помощью туннеля HTTPS, зарегистрируйте общедоступный URL-адрес обратного вызова и проверьте токен в Meta, подпишитесь на сообщения, а затем отправьте тестовое сообщение.
Что должна вернуть конечная точка проверки веб-перехватчика WhatsApp?
Для действительного запроса GET, где Hub.mode соответствует подписке и Hub.verify_token соответствует, верните значение Hub.challenge в виде обычного текста с HTTP 200.
Как проверить POST-запросы веб-перехватчика WhatsApp?
Вычислите HMAC-SHA256 по точному необработанному телу запроса с помощью Meta App Secret, добавьте к шестнадцатеричному дайджесту префикс sha256= и безопасно сравните его по времени с X-Hub-Signature-256.
Почему мой проверенный веб-перехватчик WhatsApp не получает никаких событий?
Проверка обратного вызова не подписывает автоматически каждое поле. Убедитесь, что учетная запись WhatsApp Business подписана на сообщения и что ваш тестовый отправитель и номер телефона доступны приложению.