Serverless-обработчики вебхуков для API и провайдеров
Stripe, GitHub, Slack, Shopify: проверяйте подпись по сырому телу запроса, отвечайте до таймаута провайдера, выносите медленную работу в асинхронную обработку, закладывайте идемпотентность на случай повторных доставок и сохраняйте трассы, когда ретраи накладываются друг на друга.
Обновлено: 2026-06-28
Кратко
Суть ответа
Serverless-обработчики вебхуков для API и провайдеров. Отдельные функции под каждого провайдера снижают случайное связывание между обработчиками вебхуков.
Когда подходит
- Интеграции SaaS
- Платёжные провайдеры
- События Git-хостинга
На что обратить внимание
- Пропуск проверки подписи «на время» оставляет эндпоинт открытым для поддельных payload — задолго после того, как об упрощении забыли.
- Синхронные записи в базу до ответа 200 OK провоцируют повторную доставку от провайдера — и дубли записей — при любом всплеске задержки на следующем шаге.
Нагрузка и где ломается
Типичные сбои вебхуков
Повторные доставки и медленные вызовы следующих систем заканчиваются таймаутами, если с самого начала не заложить идемпотентность и асинхронную передачу работы.
Тяжёлые записи до ответа 2xx означают повторную доставку — и двойное списание или двойную запись, если не каждый путь идемпотентен.
Один перегруженный эндпоинт тянет вниз не связанный с ним пользовательский трафик.
Когда простых рецептов недостаточно
Типичные упрощения
Пропуск проверки подписи «на время» оставляет эндпоинт открытым для поддельных payload — задолго после того, как об упрощении забыли.
Синхронные записи в базу до ответа 200 OK провоцируют повторную доставку от провайдера — и дубли записей — при любом всплеске задержки на следующем шаге.
Как помогает Inquir
Вебхук: проверить, быстро ответить, продолжить асинхронно
Отдельные функции под каждого провайдера снижают случайное связывание между обработчиками вебхуков.
Пайплайны продолжают работу после быстрого HTTP-ответа, поэтому таймауты провайдера никогда не блокируют идемпотентные записи.
Что вы получаете
Инструменты шлюза для вебхуков: авторизация, трассы, тенанты
Авторизация на маршруте
API-ключи или свои схемы авторизации на каждый входящий маршрут — до запуска кода обработчика.
Трассировка
Трассы показывают, на каком этапе случился сбой — проверка подписи, постановка в очередь или запись, — и ретраи отлаживаются по фактам, а не по догадкам.
Маршрутизация тенантов
Аккуратно разводите хосты или префиксы путей, когда у каждого клиента свой URL вебхука.
Что дальше
Как обрабатывать вебхуки на Inquir Compute
Проверка подписи, ответ в пределах таймаута провайдера, идемпотентные записи.
Проверить сырой запрос и дедуплицировать по ID события
Отклоняйте неверные подписи и блокируйте дубликаты до любых изменений состояния.
Вернуть успех в окне таймаута
Подтверждайте получение быстро, чтобы провайдер не делал лишних повторных доставок.
Поставить тяжёлую работу в очередь и писать идемпотентно
Продолжайте асинхронно и держите побочные эффекты безопасными для повторного проигрывания.
Пример кода
Паттерны реализации под конкретного провайдера
У реальных провайдеров свои заголовки подписи и ограничения по таймауту. Заводите отдельную функцию на провайдера — логика проверки остаётся маленькой и удобной для ревью.
import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); export async function handler(event) { const rawBody = event.body ?? ''; const sig = event.headers['stripe-signature'] ?? ''; let evt; try { evt = stripe.webhooks.constructEvent(rawBody, sig, process.env.STRIPE_WEBHOOK_SECRET); } catch (err) { return { statusCode: 400, body: `Webhook Error: ${err.message}` }; } const isNew = await db.upsertWebhookEvent(evt.id, evt.type); if (!isNew) return { statusCode: 200, body: 'duplicate' }; await global.durable.startNew('process-payment', undefined, { eventId: evt.id, type: evt.type, obj: evt.data.object }); return { statusCode: 200, body: 'ok' }; }
import { createHmac } from 'node:crypto'; export async function handler(event) { const rawBody = event.body ?? ''; const hmac = event.headers['x-shopify-hmac-sha256']; const digest = createHmac('sha256', process.env.SHOPIFY_WEBHOOK_SECRET).update(rawBody).digest('base64'); if (hmac !== digest) return { statusCode: 401, body: 'invalid signature' }; const topic = event.headers['x-shopify-topic']; // e.g. 'orders/create' const order = JSON.parse(rawBody); await global.durable.startNew('process-order', undefined, { topic, orderId: order.id }); return { statusCode: 200, body: '' }; }
Когда подходит
Когда подходит
Когда это уместно
- Интеграции SaaS
- Платёжные провайдеры
- События Git-хостинга
Когда лучше выбрать другое
- Двунаправленные сокеты — другой транспорт
Частые вопросы
Частые вопросы
Почему сырое тело нельзя трогать до проверки HMAC?
Провайдеры подписывают точные байты; пересериализация JSON или смена кодировки меняет payload и даёт ложные ошибки «invalid signature».
Делать ли тяжёлую работу до ответа 200?
Нет — подтвердите получение в пределах таймаута провайдера, а работу продолжите в пайплайне или фоновой функции, чтобы ретраи не дублировали дорогие побочные эффекты.
Как обрабатывать повторные доставки вебхуков?
Считайте ID события у провайдера ключом идемпотентности: фиксируйте «уже видели» до изменения данных — тогда доставка «как минимум один раз» остаётся безопасной.