Вебхуки: подписанные payload и повторные попытки

Backend2026-11-08TryQuickToolBox

Вы только что интегрировали вебхук платежного провайдера. В документации сказано: «мы отправим POST на ваш эндпоинт, когда платеж пройдет успешно». Просто, правда? А потом вы замечаете дублирующиеся события, пропущенные уведомления и предупреждение о безопасности насчет поддельных запросов. Вебхуки просты в концепции, но коварны в продакшене. В этой статье объясняется, как правильно реализовать вебхуки с использованием подписанных payload и повторных попыток, чтобы вы могли строить надежные и безопасные интеграции.

Что такое вебхуки?

Вебхук — это обратный API: вместо того чтобы ваше приложение опрашивало сервис на предмет обновлений, сервис отправляет события на ваш эндпоинт через HTTP POST. Это эффективно и почти в реальном времени. Типичные применения включают уведомления о платежах, триггеры CI/CD и интеграции с чатами.

Но вебхуки создают проблемы: как узнать, что запрос действительно пришел от провайдера? Что если ваш сервер недоступен? Что если одно и то же событие приходит дважды? Давайте разберемся с этим по порядку.

Защита вебхуков с помощью подписанных payload

Любой может отправить HTTP-запрос на ваш эндпоинт. Без проверки злоумышленники могут подделать события, что приведет к мошенничеству или повреждению данных. Стандартное решение — подписывать payload общим секретом.

Как работают HMAC-подписи

Провайдер и вы используете общий секретный ключ. Для каждого события провайдер вычисляет HMAC (код аутентификации сообщения на основе хеша) тела запроса с использованием этого секрета и отправляет подпись в заголовке (например, X-Signature). Вы пересчитываете HMAC на своей стороне и сравниваете. Если они совпадают, запрос подлинный и не изменен.

Вот минимальный пример на Python с использованием Flask:

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = b'your_shared_secret'

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Signature')
    if not signature:
        abort(400, 'Missing signature')
    body = request.get_data()
    expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        abort(401, 'Invalid signature')
    # Process the event
    return '', 200

Всегда используйте hmac.compare_digest для сравнения за постоянное время, чтобы предотвратить timing-атаки. Кроме того, проверяйте подпись до разбора JSON-тела, чтобы избежать ненужной обработки.

Лучшие практики для подписей

Обработка сбоев с помощью повторных попыток

С точки зрения провайдера вебхуки — это «выстрелил и забыл», но ваш эндпоинт может быть временно недоступен. Чтобы обеспечить доставку, провайдеры реализуют политики повторных попыток. Как получатель, вы должны быть готовы к повторам и дубликатам.

Стратегии повторов, используемые провайдерами

Большинство провайдеров повторяют с экспоненциальной задержкой: например, сразу, затем через 1 минуту, 5 минут, 30 минут и т.д., вплоть до максимального числа попыток (часто 5–10). Некоторые также позволяют настроить расписание повторов. Точное поведение различается, поэтому читайте документацию провайдера.

Проектирование эндпоинта для повторных попыток

Ваш эндпоинт должен:

Пример проверки идемпотентности на Python:

processed_events = set()  # In production, use a database or cache

@app.route('/webhook', methods=['POST'])
def webhook():
    # ... signature verification ...
    event = request.json
    event_id = event['id']
    if event_id in processed_events:
        return '', 200  # Already processed
    # Process event
    processed_events.add(event_id)
    return '', 200

Тестирование реализации вебхуков

Перед запуском в продакшен тщательно протестируйте ваш эндпоинт:

  1. Симулируйте подписанные запросы: напишите модульные тесты, которые генерируют валидные и невалидные подписи.
  2. Тестируйте сценарии повторов: симулируйте сбои и убедитесь, что ваш эндпоинт обрабатывает дубликаты.
  3. Используйте инструменты провайдера: многие провайдеры предлагают CLI или панель управления для отправки тестовых событий.
  4. Мониторинг и логирование: логируйте входящие события (без конфиденциальных данных) и настройте оповещения о сбоях подписи или высоком уровне ошибок.

Сравнение: опрос vs вебхуки

АспектОпросВебхуки
ЗадержкаЗависит от интервала (например, минуты)Почти в реальном времени
Нагрузка на серверВысокая (частые запросы)Низкая (только по событиям)
СложностьПросто реализоватьТребуется проверка подписи и обработка повторов
НадежностьВы контролируете повторыПровайдер контролирует повторы; вы должны обрабатывать дубликаты

FAQ

Как проверить подпись вебхука?

Вычислите HMAC сырого тела запроса с использованием общего секрета и сравните его с заголовком подписи с помощью функции сравнения за постоянное время. Всегда проверяйте перед обработкой payload.

Что должен возвращать мой эндпоинт для подтверждения вебхука?

Возвращайте код состояния 2xx (например, 200 OK) как только вы получили и поставили событие в очередь на обработку. Избегайте длительной обработки в обработчике запроса, чтобы предотвратить тайм-ауты.

Как обрабатывать дублирующиеся доставки вебхуков?

Используйте ключ идемпотентности из payload события (или уникальный ID события) для отслеживания обработанных событий. Храните эти ID в базе данных или кэше и пропускайте обработку, если ID уже встречался ранее.

Готовы протестировать ваши payload вебхуков? Используйте наш JSON Formatter для форматирования и валидации JSON-тела входящих вебхуков, что упрощает отладку.