Skip to main content

Webhooks (Асинхронні вебхуки)(Вебхуки: Асинхронна взаємодія сервісів)

Архітектурний патерн асинхронної міжсервісної взаємодії (Event-driven Push), за якого провайдер події надсилає HTTP POST-запит із даними на зареєстрований URL споживача при настанні системної події.

1. Огляд концепції та системна проблема

Традиційна інтеграція систем через постійне опитування (Polling / Short Polling) страждає від двох взаємовиключних проблем:

  1. Висока затримка реакції (Latency): Якщо клієнт перевіряє зміни кожні 60 секунд, дані доставляються в середньому із запізненням у 30 секунд.
  2. Колосальне марнування обчислювальних ресурсів: 99% HTTP-запитів повертають порожню відповідь 304 Not Modified або [], перевантажуючи базу даних та мережевий стек.

Webhooks (Зворотні виклики через HTTP) змінюють парадигму з Pull на Push. Замість того щоб запитувати: "Чи є нові дані?", споживач реєструє свій URL (Webhook Listener). Щойно в системі провайдера трапляється подія (успішна транзакція Stripe, новий комміт у GitHub, вхідне повідомлення Telegram), провайдер надсилає HTTP POST-запит із JSON payload безпосередньо на сервер підписника.

Традиційний Polling (Марнування ресурсів):
Клієнт  ---> GET /events?since=... ---> Сервер (Порожньо: [])
Клієнт  ---> GET /events?since=... ---> Сервер (Порожньо: [])
Клієнт  ---> GET /events?since=... ---> Сервер (Нова подія!)

Event-Driven Webhook (Миттєвий Push):
Провайдер подій (Stripe / GitHub / Telegram)
       |
       |  Подія: charge.succeeded (Event Payload + HMAC підпис)
       |  HTTP POST https://api.mysite.com/webhooks/stripe
       v
Споживач (Валідація підпису -> 200 OK -> Фонова черга RabbitMQ/Redis)

2. Архітектурна таксономія та ментальна модель

Життєвий цикл вебхука спирається на три архітектурні опори:

  1. Криптографічна автентифікація (HMAC Signature):
    • Провайдер генерує криптографічний хеш від тіла запиту (raw body) за допомогою спільного секретного ключа (Webhook Secret), додаючи його в заголовок (наприклад, Stripe-Signature або X-Hub-Signature-256).
    • Споживач обчислює такий самий хеш і валідує його, виключаючи ризик підробки запиту зловмисником.
  2. Ідемпотентність та дедуплікація (Idempotency Engine):
    • Кожна подія має глобальний унікальний ID (evt_xxxxxxxxxx).
    • Споживач атомарно перевіряє наявність ID у кеші/БД (наприклад, через INSERT ... ON CONFLICT DO NOTHING або Redis SET key NX EX 86400).
  3. Політика гарантованої доставки та повторів (Retry & Dead-Letter Queue):
    • У разі недоступності сервера споживача провайдер повторює запити за експоненціальною шкалою (Exponential Backoff з джитером): через 5 хв, 30 хв, 2 год, 12 год.
    • Події, що не вдалося обробити після максимальної кількості спроб, направляються в Dead-Letter Queue (DLQ) для ручного аналізу інженерами.

3. Технічний пайплайн та внутрішня механіка

Виробничий обробник із верифікацією HMAC (Node.js / Express / TypeScript)

[!IMPORTANT] Для перевірки підпису необхідно отримувати необроблене тіло запиту (raw buffer), а не розпарсений об'єкт req.body, інакше хеш не зійдеться через розбіжності пробілів і сортування ключів JSON.

import express, { Request, Response } from "express";
import crypto from "crypto";

const app = express();

// Отримання raw buffer для ендпоінтів вебхуків
app.post(
  "/api/v1/stripe-webhook",
  express.raw({ type: "application/json" }),
  async (req: Request, res: Response) => {
    const signature = req.headers["stripe-signature"] as string;
    const secret = process.env.STRIPE_WEBHOOK_SECRET!;

    if (!signature) {
      return res.status(400).send("Missing signature header");
    }

    // 1. Криптографічна валідація підпису через HMAC SHA-256
    const computedHmac = crypto
      .createHmac("sha256", secret)
      .update(req.body)
      .digest("hex");

    // Безпечне порівняння за фіксований час
    const isValid = crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(computedHmac)
    );

    if (!isValid) {
      return res.status(401).send("Invalid signature");
    }

    const payload = JSON.parse(req.body.toString());
    const eventId = payload.id;

    // 2. Ідемпотентна перевірка дедуплікації
    const isNew = await redis.set(`webhook:processed:${eventId}`, "1", "NX", "EX", 86400);
    if (!isNew) {
      // Подію вже оброблено — повертаємо 200 OK без повторного виконання
      return res.status(200).json({ status: "already_processed" });
    }

    // 3. Відправка важкої роботи в чергу BullMQ
    await eventQueue.add("process_payment", payload);

    // 4. Миттєве підтвердження отримання
    return res.status(200).json({ received: true });
  }
);

4. Практичні інженерні сценарії в продакшені

01. Платіжний шлюз для AI SaaS (Stripe / Paddle / LiqPay)

Користувач сплачує підписку на Pro-план. Платіжна система генерує подію invoice.payment_succeeded. Вебхук миттєво оновлює статус користувача в базі даних, скидає ліміти токенів у Redis та надсилає вітальний лист, дозволяючи користувачеві продовжити роботу без перезавантаження сторінки.

02. Тригер автономного CI/CD пайплайну (GitHub Webhooks)

Інженер виконує git push у гілку main. GitHub надсилає payload із хешем комміту на власний сервер Coolify або ArgoCD. Сервер перевіряє секрет, клонує змінений код, запускає unit-тести в ізольованому Docker-контейнері та оновлює production-сервіс за стратегією Blue/Green.

03. Асинхронний збір результатів інференсу важких AI-моделей

При виклику моделей генерації відео (Runway, Kling) або тренування LoRA запит триває кілька хвилин. Замість тримання відкритого HTTP-з'єднання клієнт передає webhook_url. Щойно кластер GPU завершує генерацію, він надсилає вебхук із фінальним S3-посиланням на згенероване медіа.


5. Підводні камені, типові помилки та безпека

  1. Парсинг JSON перед верифікацією підпису: Якщо перед перевіркою підпису відпрацював стандартний мідлвар express.json(), серіалізатор може змінити порядок полів, екранування Unicode-символів або форматування чисел, що призведе до невідповідності контрольної суми HMAC та відмови Invalid Signature.
  2. Синхронне блокування виконання (Webhook Deadlock): Виконання важких транзакцій або тривалих HTTP-запитів до зовнішніх API всередині обробника вебхука гарантовано призводить до таймауту на стороні провайдера. Повертайте 200 OK миттєво після валідації підпису та збереження в чергу.
  3. Відсутність валідації часових міток (Replay Attacks): Зловмисник, що перехопив легітимний підписаний вебхук-запит, може надіслати його повторно через годину. Провайдери додають у підпис часову мітку (timestamp). Перевіряйте, щоб різниця між часом запиту та системним годинником сервера не перевищувала допустиме вікно (наприклад, 5 хвилин).
/ Часті запитанняSchema.org FAQPage

FAQ: Webhooks (Асинхронні вебхуки)

Класичне порівняння рядків через оператори == або === переривається на першому невідповідному символі. Зловмисник може вимірювати затримку відповіді сервера з наноточністю (Timing Attack) і побайтово підібрати валідний хеш підпису. Функція timingSafeEqual виконує порівняння за фіксований час, гарантуючи криптографічну стійкість.
/ Внутрішня перелінковка
Всі терміни
VPS & DevOps

Telegram Bot API (API ботів Telegram)

Офіційний HTTP-інтерфейс платформи Telegram, що дозволяє створювати автономних чат-ботів, AI-асистентів, інтерфейси Telegram Mini Apps (TMA) та канали інтерактивного сповіщення.

Читати термін
VPS & DevOps

Крон-планувальники (Cron Schedulers & Systemd Timers)

Системні демони (Linux cron, systemd timers) та розподілені черги (BullMQ, Temporal), що забезпечують гарантований запуск періодичних інженерних задач, бекапів, синхронізації даних та AI-агентів за розкладом.

Читати термін
VPS & DevOps

Rate Limiting (Обмеження частоти запитів та захист API)

Системний механізм контролю інтенсивності вхідного та вихідного трафіку (Token Bucket, Sliding Window) для захисту бекенду від вичерпання ресурсів, брутфорсу, Layer 7 DDoS та фінансового овердрафту на AI-ендпоінтах.

Читати термін
VPS & DevOps

Зворотний проксі (Nginx, Caddy, Traefik)

Проміжний серверний архітектурний шар, який приймає зовнішній інтернет-трафік (порти 80/443), виконує термінацію SSL/TLS, стиснення (Brotli/Gzip), кешування статики та безпечно маршрутизує запити до внутрішніх додатків.

Читати термін