Усі статті
Події мобільних повідомлень проходять перевірку вебхука Meta й захищеним тунелем надходять до застосунку на localhost.
WhatsApp Cloud APIMeta webhookslocalhostwebhook security

Тестування вебхуків WhatsApp Cloud API на localhost

Щоб перевірити вебхук WhatsApp Cloud API на локальному хості, відкрийте свою локальну кінцеву точку через HTTPS, застосуйте перевірочний виклик Meta's GET, а потім перевірте кожен запит POST X-Hub-Signature-256 проти необробленого тіла. Зареєструйте URL-адресу тунелю у своїй програмі Meta, підпишіться на бізнес-акаунт WhatsApp на messages та надішліть тестове повідомлення, щоб отримати реальне корисне навантаження без розгортання.

Вебхуки WhatsApp використовують два різні процеси перевірки

Найважливіша відмінність полягає в тому, що налаштування та доставка вебхуку є автентифіковані інакше. Під час налаштування Meta надсилає запит GET, що містить hub.mode, hub.verify_token і hub.challenge. Ваша кінцева точка порівнює маркер перевірки та повертає виклик у вигляді звичайного тексту. Пізніше доставка подій здійснюється за запитами POST; вони мають бути автентифіковані шляхом перевірки підпису HMAC, створеного за допомогою вашого Meta App Secret.

Маркер перевірки – це випадковий рядок, який ви вибираєте; це не маркер доступу WhatsApp і не App Secret. Повернення виклику підтверджує контроль над кінцевою точкою зворотного виклику. Він не автентифікує майбутні запити POST. Meta's офіційний посібник із вебхуку WhatsApp охоплює конфігурацію зворотного виклику, підписки та поля webhook.

Створення кінцевої точки маршрутизатора програми 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. У Meta на інформаційній панелі розробника відкрийте сторінку конфігурації продукту 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" з лапками. If the token is wrong, 403 is appropriate. Не реєструйте параметри запиту, оскільки там відображається маркер перевірки.

Зрозумійте корисне навантаження повідомлень, перш ніж писати бізнес-логіку

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 над необробленими байтами запиту за допомогою Meta App Secret. Постійний або тимчасовий маркер доступу WhatsApp використовується для викликів Graph API; це не ключ HMAC. Використовуйте порівняння в постійному часі та відхиляйте відсутній підпис.

Не вмикайте локальну перевірку. Будь-хто, хто дізнається URL-адресу тунелю, може ПУБЛІКУВАТИ до нього довільний JSON. Без перевірки підроблена подія може викликати автоматичні відповіді, змінити записи CRM або розкрити стан клієнта. Змініть секрет додатка, якщо його було зафіксовано, надруковано або надіслано випадково.

Швидко підтверджуйте та видаляйте дублікати повідомлень

Повертайте 200 після автентифікації та тривалого розміщення події в черзі. Не чекайте, поки завантажуєте медіафайли, телефонуєте до LLM або оновлюєте кілька служб. Постачальники повторюють спробу доставки, коли підтвердження не вдається, а неоднозначність мережі означає, що дублікати є нормальним явищем.

Використовуйте повідомлення WhatsApp id як ключ ідемпотентності для вхідних повідомлень і об’єктів статусу. Встановіть унікальне обмеження навколо оброблених ідентифікаторів. Зміни статусу можуть законно переходити від «Надіслано» до «Доставлено для читання», тому видаліть дублікати кожного відповідного переходу, не відкидаючи пізніший стан.

Вирішіть проблеми з налаштуванням веб-хука WhatsApp

Не вдалося перевірити URL-адресу зворотного виклику

Перевірте маршрут GET через загальнодоступну URL-адресу. Переконайтеся, що він приймає GET, порівнює точний маркер перевірки та відповідає лише викликом. Переспрямування, проміжне програмне забезпечення автентифікації, перезапис локалі або оболонка JSON можуть порушити перевірку. Переконайтеся, що змінну середовища завантажено запущеним процесом розробника.

Перевірка проходить успішно, але повідомлення не надходять

Перевірка зворотного дзвінка сама по собі не підписує обліковий запис WhatsApp Business на поля. Підтвердьте підписку messages на інформаційній панелі та те, що номер телефону належить очікуваній програмі й обліковому запису. Надішліть повідомлення від дозволеного одержувача, якщо програма все ще перебуває в режимі розробки.

Кожен POST не проходить перевірку підпису

Звичайними причинами є використання маркера доступу замість App Secret, хешування проаналізованого JSON, пропуск префікса sha256= або порівняння різних кодувань. Реєструйте довжину тіла журналу та наявність заголовка, але ніколи не друкуйте секретне або повне корисне навантаження клієнта.

Текстові повідомлення працюють, але не вдається обробити медіа

Медіа-сповіщення містять ідентифікатор, а не обов’язково байти файлу. Отримайте медіафайли через API Graph із дійсним маркером доступу, а потім завантажте їх. Зберігайте цей повільніший робочий процес за межами шляху підтвердження вебхука.

Локальна кінцева точка бачить повторювані події

Перевірте стан відповіді та затримку, додайте тривалу ідемпотентність і відтворюйте одну зафіксовану подію після кожного виправлення. Посібник із відтворення вебхука показує, як уникнути надсилання нового справжнього повідомлення для кожної зміни коду.

Захистіть дані клієнтів під час локальних тестів

  • Використовуйте тестові номери телефонів і синтетичні розмови, де це можливо.
  • Відредагуйте номери телефонів, текст повідомлення, медіа-URL, контакти та імена профілів із журналів.
  • Зберігайте App Secret, маркери доступу та перевіряйте маркери лише в ігнорованих файлах середовища або секретному менеджері.
  • Обмежте, хто може переглядати захоплення тунелів, і видаліть їх після сеансу налагодження.
  • Перед тим перевірте ідентифікатори об’єктів, полів і облікових записів виконання бізнес-дій.

Тунель пришвидшує ітерацію, але також передає особисті дані у формі виробництва на машину розробника. Застосуйте елементи керування з контрольного списку безпеки тунелю перед тестуванням із реальними користувачами.

Поширені запитання

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