Технологии

Webhook что это и зачем он нужен твоему приложению

19 минАктуально на 22 августа 2026

Webhook что это и зачем он нужен

Ты подключил приём оплаты, пользователь заплатил — и твоё приложение об этом не знает. Доступ не открылся, хотя деньги списались. Первая мысль обычно «сломался код», но чаще проблема в другом: приложение просто никак не узнаёт о том, что произошло на стороне платёжного сервиса. Именно для этого случая придуман webhook. Разберём, что это такое, чем он отличается от привычного API-запроса и как проверить, что он реально работает, до того как показывать проект пользователям.

Содержание
  1. Webhook что это простыми словами
  2. Чем webhook отличается от API-запроса
  3. Где webhook реально используется на курсе
  4. Как принять webhook на своей стороне
  5. Повторные попытки и идемпотентность
  6. Как проверить webhook до выкладки в прод
  7. Безопасность вебхуков
  8. Типичная ошибка новичка
  9. Частые вопросы

Webhook что это простыми словами

Webhook — это способ, которым один сервис сообщает другому о произошедшем событии, отправляя запрос сам, без напоминаний с твоей стороны. Слово составлено из «web» и «hook» — «веб-крючок»: твоё приложение вешает крючок на чужой сервис, и когда там что-то случается, сервис сам за этот крючок дёргает.

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

Житейская аналогия ближе, чем кажется. Обычный запрос — это когда ты звонишь в службу доставки и спрашиваешь, где твой заказ. Если ответ «ещё в пути», перезваниваешь через час и спрашиваешь снова. Webhook — это когда служба доставки сама звонит тебе в момент, когда курьер подъехал к подъезду. Тебе не нужно держать телефон и звонить каждые десять минут — тебе сообщат, когда действительно есть новость.

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

Чем webhook отличается от API-запроса

Если ты уже разбирался, что такое API, знаешь: API — это набор правил, по которым программы обмениваются данными. Обычный запрос к API ты инициируешь сам, когда тебе это нужно. Хочешь узнать курс валют — отправляешь запрос к сервису курсов. Хочешь список товаров — запрашиваешь список товаров. Инициатива всегда твоя, и сервис отвечает только тогда, когда его спросили.

С webhook инициатива переходит на другую сторону. Ты не спрашиваешь «оплата прошла?» раз в несколько секунд — вместо этого платёжный сервис сам присылает запрос в момент, когда оплата действительно прошла. Разница не техническая, а в том, кто начинает разговор.

Здесь работает удобная формула. Обычный API-запрос ты вызываешь, когда нужно тебе. Webhook вызывает внешний сервис, когда нужно ему — сообщить о событии. У этих двух направлений даже роли разные: в первом случае твоё приложение — клиент, который спрашивает, во втором — сервер, который принимает.

Именно поэтому webhook требует подготовки заранее. Обычный API-запрос можно отправить в любой момент прямо из кода — не нужно ничего настраивать заблаговременно. А для webhook твоё приложение обязано заранее подготовить endpoint — адрес, который принимает входящие запросы, — и сообщить этот адрес сервису, который будет присылать уведомления. Без готового endpoint внешнему сервису физически некуда отправлять данные о событии.

Разница по шагам:

Обычный API-запрос Webhook
Кто начинает Твоё приложение Внешний сервис
Когда Когда тебе нужно Когда произошло событие
Что нужно заранее Ничего особенного Готовый endpoint на твоей стороне
Аналогия Ты звонишь и спрашиваешь Тебе звонят сами

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

flowchart TB
    A["Событие у сервиса: оплата прошла"] --> B["Сервис отправляет POST на адрес приложения"]
    B --> C["Приложение проверяет подпись запроса"]
    C --> D["Приложение открывает доступ пользователю"]

Где webhook реально используется на курсе

На практике чаще всего с webhook сталкиваются при работе с платёжными сервисами. Когда ты подключаешь приём платежей через ЮKassa или настраиваешь приём платежей через CloudPayments, эти сервисы присылают webhook в момент, когда статус платежа меняется — например, когда оплата прошла успешно.

Зачем это вообще нужно, если можно просто перенаправить пользователя обратно на сайт после оплаты и там показать «спасибо за покупку»? Дело в том, что возврат пользователя на сайт — событие ненадёжное. Человек может закрыть вкладку раньше, чем страница успеет перенаправить его обратно. Может потерять интернет-соединение на середине пути. Может просто передумать нажимать «вернуться на сайт» и уйти проверять почту. Приложение, которое полагается только на возврат пользователя, в этих случаях так и не узнает, что оплата на самом деле прошла.

Webhook работает независимо от того, что делает пользователь в браузере. Платёжный сервис обрабатывает деньги на своей стороне и, как только статус платежа определился, отправляет запрос напрямую на сервер твоего приложения — в обход браузера пользователя. Твоё приложение получает эту информацию, даже если человек давно закрыл вкладку и уехал на дачу без интернета.

Практический результат — доступ открывается по факту оплаты, а не по факту того, что пользователь вернулся на страницу успеха. Это надёжнее и снимает целый класс проблем поддержки вида «я оплатил, а доступа нет».

Похожая логика применима не только к оплате. Сервисы доставки сообщают о смене статуса заказа. Мессенджеры сообщают о новом сообщении в чат-боте. Системы уведомлений сообщают об истечении срока подписки. Общий принцип один: что-то случилось на стороне внешнего сервиса, и он сам сообщает об этом тебе, а не заставляет тебя гадать и переспрашивать.

Как принять webhook на своей стороне

Чтобы принять входящий webhook, приложению нужен endpoint — обычный роут, который слушает входящие POST-запросы по конкретному адресу, например /api/webhook/payment. Когда ты подключаешь платёжный сервис, в его настройках указываешь именно этот адрес — сервис запомнит его и будет присылать туда уведомления обо всех событиях.

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

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

Внутри тела запроса сервис обычно присылает несколько типовых полей: тип события (например «платёж завершён»), идентификатор объекта, к которому событие относится (номер заказа или платежа), и статус этого события. Точный набор полей и их названия у каждого сервиса свои и описаны в его документации — здесь важно не заучивать конкретные имена полей, а понимать общий принцип: из тела запроса нужно достать идентификатор, по которому находится нужная запись в твоей базе, и статус, который решает, что с этой записью делать дальше.

Повторные попытки и идемпотентность

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

Отсюда следует правило, которое легко упустить: обработчик должен уметь получить одно и то же событие дважды и не наделать от этого лишних действий. Такое свойство называется идемпотентностью — повторный запуск с теми же данными приводит к тому же результату, что и один запуск. На практике это решается просто: перед тем как открыть доступ или изменить состояние, обработчик проверяет, не обработано ли уже это событие раньше, например по идентификатору платежа. Если запись уже отмечена оплаченной, повторный webhook с тем же идентификатором просто подтверждается ответом и не запускает повторное открытие доступа.

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

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

Как проверить webhook до выкладки в прод

Загвоздка в том, что внешний сервис отправляет webhook на публичный адрес — тот, который доступен из интернета. Пока ты разрабатываешь приложение у себя на компьютере, публичного адреса у тебя просто нет: локальный сервер отвечает только на localhost, и снаружи до него не достучаться.

Получается замкнутый круг — код обработчика написан, а проверить его в деле нельзя, потому что платёжный сервис не может отправить запрос на компьютер, у которого нет адреса в интернете. Ждать полной выкладки в прод, чтобы протестировать webhook впервые, — плохая идея: любая ошибка в обработчике обнаружится уже на реальных деньгах реальных пользователей.

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

Порядок действий на практике такой: запускаешь локальный сервер → запускаешь ngrok, получаешь временный публичный адрес → указываешь этот адрес в настройках сервиса как адрес для webhook → делаешь тестовое событие (например тестовый платёж) → смотришь в логах, дошёл ли запрос и правильно ли обработчик на него отреагировал. Только после того как эта цепочка отработала без сюрпризов, есть смысл переключать адрес на боевой и выкладывать код в прод.

Безопасность вебхуков

Endpoint для webhook — обычный публичный URL, а значит, отправить на него запрос может кто угодно, кто этот адрес узнал, а не только настоящий платёжный сервис. Если приложение слепо доверяет содержимому любого запроса, который пришёл на адрес webhook, кто-то может подделать сообщение вида «оплата прошла успешно» и получить доступ бесплатно.

Поэтому сервисы, которые отправляют webhook, добавляют к запросу подпись — signature. Это специальное значение в заголовке запроса, рассчитанное на основе секретного ключа, который известен только сервису и твоему приложению. Подпись строится так, что подделать её без знания секретного ключа невозможно, даже если знать в точности формат самого запроса.

Обработчик обязан проверить подпись раньше, чем начинать доверять данным внутри запроса. Логика простая: приложение пересчитывает подпись самостоятельно, используя тот же секретный ключ, и сравнивает результат с подписью, которая пришла в заголовке. Совпало — запрос действительно от нужного сервиса, можно обрабатывать. Не совпало — запрос игнорируется, потому что это либо подделка, либо повреждённые данные.

Секретный ключ для проверки подписи выдаётся при подключении сервиса, вместе с остальными ключами доступа. Хранить его нужно так же бережно, как любой другой секретный ключ — в переменных окружения, а не прямо в коде.

Типичная ошибка новичка

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

Правильная логика — всегда проверять статус, который лежит внутри самого тела запроса, а не полагаться на то, что запрос вообще пришёл. Если открывать доступ по одному факту получения webhook без проверки статуса, пользователь может получить доступ даже тогда, когда его платёж на самом деле отменился или завершился ошибкой.

Практическое правило: обработчик webhook сначала проверяет подпись, потом читает поле статуса из тела запроса, и только если статус означает успех, обновляет данные приложения. Все прочие статусы — повод залогировать событие и ничего не менять в состоянии пользователя.

Частые вопросы

Webhook — это то же самое, что API?

Нет. API — это в целом способ, которым программы обмениваются данными, а webhook — частный случай такого обмена, где инициатором выступает не твоё приложение, а внешний сервис. У webhook та же техническая база, что у обычного запроса — HTTP, JSON в теле, — но направление и момент отправки другие.

Нужен ли webhook в каждом приложении?

Нет, только там, где есть события на стороне внешнего сервиса, о которых приложению важно узнать без задержки — оплата, изменение статуса заказа, входящее сообщение. Если приложение работает только с собственными данными без внешних сервисов, webhook не понадобится.

Что произойдёт, если приложение не ответит на webhook?

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

Можно ли протестировать webhook без ngrok?

Формально да, если у сервера уже есть постоянный публичный адрес — например тестовый сервер на хостинге. Но на этапе локальной разработки такого адреса обычно нет, и ngrok остаётся самым быстрым способом получить временный публичный адрес именно для проверки.

Обязательно ли проверять подпись, если проект учебный?

Да, привычку стоит закладывать сразу, даже в учебном проекте. Пропуск проверки подписи — не мелкая недоработка, а дыра, через которую можно получить платный доступ без оплаты. Гораздо проще один раз написать проверку правильно, чем потом переучиваться на боевом проекте.

Как понять, что webhook действительно настроен верно?

Проверка та же, что описана выше: локальный сервер плюс ngrok, тестовое событие на стороне сервиса, проверка в логах, что запрос дошёл, подпись прошла проверку, а состояние приложения обновилось так, как ожидалось. Если все три шага отработали на тестовых данных, можно переключаться на боевой адрес.

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

Читай дальше

Все статьи

Не просто статьи — тебя доведут до результата

В практикуме за 2499 ₽ рядом живая команда практикующих разработчиков и маркетологов: ведём по шагам до твоего работающего приложения. Не «ролики и сам разбирайся» — помогаем на каждом затыке.

Перейти к практикуму
Все статьи Ещё: технологии и архитектура