Вебхуки: подписанные данные и повторы
Вы только что интегрировали платёжный шлюз с помощью вебхуков. В тестовой среде всё работает, но в продакшене события иногда теряются или вы получаете дублирующиеся уведомления, приводящие к двойным списаниям. Корень проблемы часто кроется в том, как вы обрабатываете подписанные данные и повторы. В этой статье объясняется механика вебхуков и приводятся практические шаги для построения надёжной и безопасной интеграции.
Что такое вебхуки?
Вебхуки — это определяемые пользователем HTTP-колбэки. Когда в исходной системе происходит событие (например, обрабатывается платёж), она отправляет HTTP POST-запрос на указанный вами URL с данными события. В отличие от опроса, вебхуки передают данные почти в реальном времени, снижая задержку и нагрузку на сервер.
Однако вебхуки создают проблемы: проверка подлинности, обработка сбоев и обеспечение однократной обработки. Разберём каждую.
Подписанные данные: проверка подлинности
Поскольку конечные точки вебхуков общедоступны, любой может отправить поддельные запросы. Чтобы предотвратить это, провайдеры подписывают данные секретным ключом. Вы проверяете подпись, чтобы убедиться в подлинности запроса.
Как работают подписи HMAC
Большинство провайдеров используют HMAC (код аутентификации сообщений на основе хэша) с SHA-256. Провайдер вычисляет подпись, используя тело запроса и общий секрет, затем включает её в заголовок (например, X-Signature). Вы пересчитываете подпись на своей стороне и сравниваете.
Пример на Python:
import hmac
import hashlib
def verify_signature(payload, secret, received_signature):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_signature)
Важно: Используйте необработанное тело запроса, а не разобранный JSON, так как парсинг может изменить пробелы и нарушить подпись.
Распространённые ошибки
- Использование разобранных данных: Всегда проверяйте по необработанным байтам.
- Атаки по времени: Используйте сравнение с постоянным временем (например,
hmac.compare_digest). - Игнорирование меток времени: Некоторые провайдеры включают метку времени в подпись для предотвращения повторных атак. Проверьте, что она находится в пределах допустимого окна (например, 5 минут).
Повторы: изящная обработка сбоев
Ваша конечная точка может быть временно недоступна, или сетевые проблемы могут вызвать сбои. Надёжные системы вебхуков повторяют неудачные доставки с экспоненциальной задержкой.
Стратегии повторов
Провайдеры обычно повторяют при ответах не 2xx или тайм-аутах. Распространённые шаблоны:
| Стратегия | Описание | Пример |
|---|---|---|
| Фиксированный интервал | Повтор каждые N секунд | Каждые 30 с, до 5 раз |
| Экспоненциальная задержка | Удвоение задержки при каждой попытке | 1 с, 2 с, 4 с, 8 с... |
| Экспоненциальная с джиттером | Добавление случайности для избежания эффекта стада | 1 с ± 0,5 с, 2 с ± 1 с... |
Как получатель, вы не можете управлять политикой повторов отправителя, но можете спроектировать свою конечную точку устойчивой.
Лучшие практики для получателей
- Отвечайте быстро: Возвращайте 2xx в течение нескольких секунд. Передайте обработку фоновой задаче.
- Будьте идемпотентны: Используйте ключ идемпотентности из данных, чтобы избежать дублирующей обработки.
- Логируйте всё: Сохраняйте входящие вебхуки для отладки и воспроизведения.
- Мониторьте сбои: Настройте оповещения о повторяющихся сбоях.
Реализация идемпотентности
Дубликаты случаются: отправитель может повторить попытку из-за медленного ответа, или вы можете случайно обработать одно и то же событие дважды. Идемпотентность гарантирует, что многократная обработка события имеет тот же эффект, что и однократная.
Используйте уникальный ID события (часто предоставляется в данных или заголовках) и сохраните его в базе данных с уникальным ограничением. Перед обработкой проверьте, существует ли ID; если да — пропустите.
def process_event(event_id, data):
if EventLog.exists(event_id):
return # already processed
EventLog.create(event_id)
# process data...
Для высоконагруженных систем используйте распределённую блокировку или транзакцию базы данных, чтобы избежать состояний гонки.
Вопросы безопасности
Помимо проверки подписи, учитывайте:
- HTTPS: Всегда используйте TLS для предотвращения прослушивания.
- Список разрешённых IP: Если провайдер публикует диапазоны IP, ограничьте входящие запросы.
- Ограничение скорости: Защитите свою конечную точку от злоупотреблений.
- Валидация данных: Даже после проверки подписи проверяйте схему данных, чтобы избежать атак внедрения.
Тестирование вебхуков локально
Во время разработки вам нужен публичный URL для получения вебхуков. Инструменты вроде ngrok или localtunnel открывают ваш локальный сервер. Альтернативно, многие провайдеры предлагают CLI для пересылки событий на ваш localhost.
Симулируйте сбои для проверки обработки повторов: намеренно возвращайте ошибки 500 и наблюдайте за шаблоном повторов провайдера.
FAQ
Как проверить подпись вебхука?
Используйте HMAC с общим секретом. Вычислите хэш необработанного тела запроса и сравните его с заголовком подписи, используя функцию сравнения с постоянным временем.
Что делать, если я получаю дублирующиеся вебхуки?
Реализуйте идемпотентность, сохраняя ID событий. Проверьте, было ли событие уже обработано, прежде чем выполнять бизнес-логику.
Можно ли полагаться только на повторы вебхуков для надёжности?
Нет. Повторы помогают, но не гарантируют доставку. Для критических событий комбинируйте вебхуки с резервным опросом или очередью сообщений, которая сохраняет события.
При отладке данных вебхуков вам может понадобиться просмотреть JSON-данные. Используйте наш JSON Formatter для быстрого форматирования и валидации данных.