Вебхуки криптоплатежей: события, безопасность и надёжная обработка заказов
Вебхук — это момент, когда магазин узнаёт, что криптоплатёж окончателен. Рассказываем, как построить безопасный, идемпотентный и скучно надёжный обработчик, с картой событий и чек-листом для продакшена.

Вебхуки криптоплатежей — это HTTP-запросы, которые платёжный шлюз отправляет на ваш сервер при каждом изменении статуса счёта: перевод обнаружен, подтверждён в сети, недоплачен, переплачен или истёк. Обработчик проверяет подпись, обновляет заказ и запускает выдачу. Поскольку криптоплатежи приходят асинхронно — через минуты после того, как клиент ушёл со страницы, — источником истины является вебхук, а не браузер.
Это руководство посвящено вебхукам: какие события приходят, как их проверять, как безопасно обрабатывать одно событие, пришедшее дважды, и как всё это тестировать и мониторить. О создании счетов читайте в руководстве по интеграции API криптоплатежей.
Почему вебхуки, а не опрос или редирект?
- Редирект — не доказательство — возврат клиента на страницу «спасибо» ничего не говорит о поступлении денег.
- Опрос расточителен — запрашивать API каждые несколько секунд по каждому открытому счёту не масштабируется и всё равно запаздывает.
- Вебхук — это push — шлюз сообщает сразу, как только что-то изменилось, и прикладывает проверяемую подпись.
Какие события вы будете получать
| Событие | Значение | Что должен сделать обработчик |
|---|---|---|
| invoice.paid | Перевод обнаружен, ждём подтверждений | Показать «платёж получен, подтверждается»; пока не выдавать |
| invoice.confirmed | Набрано нужное число подтверждений | Отметить оплату и выполнить заказ |
| invoice.underpaid | Меньше ожидаемого, ниже допуска | Сообщить клиенту, сколько не хватает; ждать доплаты |
| invoice.overpaid | Пришло больше ожидаемого | Выполнить заказ; решить, что делать с разницей |
| invoice.expired | Нет корректной оплаты в срок | Вернуть зарезервированный товар; заказ остаётся неоплаченным |
Это названия событий в mistKET. В других шлюзах похожие статусы называются иначе — сопоставьте их со своими статусами заказа в одном месте.
Проверка подписи
Отправить POST на публичный URL может кто угодно, поэтому каждый вебхук должен проходить аутентификацию. mistKET подписывает каждую доставку HMAC-SHA256 и передаёт заголовки:
- X-Mistket-Event — название события.
- X-Mistket-Timestamp — Unix-время, входящее в подпись.
- X-Mistket-Signature —
sha256=и HMAC от{timestamp}.{raw body}с вашим секретом вебхука. - X-Mistket-Delivery — ID доставки для логов.
- Прочитайте сырое тело — до разбора JSON, потому что перекодирование меняет байты.
- Пересчитайте HMAC — по метке времени, точке и сырому телу с вашим секретом.
- Сравните за постоянное время — через
hash_equals,hmac.compare_digestилиcrypto.timingSafeEqual. - Отклоняйте старые метки времени — например, старше пяти минут, чтобы блокировать повторы.
- Только потом разбирайте и действуйте — и быстро возвращайте 2xx.
Конструкция следует стандартной схеме HMAC; храните секрет на сервере и меняйте его при утечке.
Выбор и защита callback-URL
Используйте отдельный HTTPS-путь, например /webhooks/mistket, независимый от обычных страниц, и передавайте его как callback при создании каждого счёта. Исключите его из CSRF-защиты, кеширования и редиректов: 301-редирект или закешированный ответ — частая причина «тихих» сбоев вебхуков. Не требуйте сессионную cookie: шлюз не авторизованный пользователь, аутентификация — это подпись HMAC. И наконец, логируйте каждую доставку с событием, номером заказа и кодом ответа, чтобы за секунды отвечать на вопрос «а вебхук пришёл?».
Идемпотентность: одно событие может прийти дважды
Если ваш эндпоинт не успел ответить или вернул ошибку, шлюз повторяет отправку с нарастающими интервалами в течение нескольких часов. Сетевые сбои тоже могут принести уже обработанное событие. Поэтому обработчик должен быть идемпотентным:
- Ключ — номер заказа — найдите заказ, на который ссылается счёт.
- Проверяйте текущий статус — если заказ уже выполнен, подтвердите получение и ничего не делайте.
- Используйте транзакцию или блокировку — чтобы две параллельные доставки не выдали товар дважды.
- Сохраняйте ID доставки — пригодится при разборе дублей.
Статусы заказа вокруг событий
Простая машина состояний предотвращает большинство ошибок: ожидает → подтверждается (по paid) → оплачен (по confirmed) → выполнен, с ветками недоплата и истёк. Разрешайте только переходы вперёд: invoice.paid, пришедший после invoice.confirmed, никогда не должен откатывать заказ назад. Поздние платежи по истёкшим счетам должны создавать запись для проверки, а не тихо переоткрывать заказ.
Неудобные случаи
- Недоплата — сообщите клиенту точную недостающую сумму и что её можно отправить на ту же платёжную страницу; частичные платежи суммируются.
- Переплата — выполните заказ, затем по своей политике решите: зачесть на баланс или вернуть разницу переводом.
- Истечение — верните товар в продажу и предложите новый счёт, если клиент ещё хочет купить.
- Не та сеть — решается поддержкой, а не кодом; чтобы этого избежать, чётко показывайте сеть на странице оплаты.
Тестирование и мониторинг
- Поднимите тестовый эндпоинт — по HTTPS, с логированием заголовков и сырых тел.
- Вызовите каждое событие — paid, confirmed, underpaid, overpaid и expired небольшими реальными платежами.
- Повторите доставку — убедитесь, что обработчик игнорирует дубль.
- Сломайте подпись — измените один байт и проверьте, что запрос отклонён.
- Смотрите логи доставки — кабинет показывает каждую попытку, код ответа и хеш транзакции.
- Настройте оповещения — всплеск ответов не 2xx означает, что заказы не выполняются.
Боты и платные сообщества устроены так же; как это ложится на чат, показано в статье приём криптоплатежей в Telegram-боте.
Вебхуки в mistKET
mistKET отправляет подписанные HMAC вебхуки для каждого статуса счёта, повторяет неудачные доставки с нарастающими интервалами и показывает в кабинете логи доставки, ожидаемые и полученные суммы и хеши транзакций. Запросы к API подписываются так же, поэтому одна вспомогательная функция покрывает оба направления.
Примеры запросов и вебхуков — в разделе для разработчиков; посмотрите тарифы и управляйте ключами через вход для продавцов. Чтобы получить доступ, подайте заявку в Telegram — @mistnetwork.


