Событие веб-хука по своей сущности
[OnWebhookUnknownEntity] Веб-хук по сущности, которую модуль не обслуживает
На более ранних версиях событие не вызывается вовсе. Подписка на него безвредна: обработчик просто никогда не будет позван.
Модуль сам умеет раскладывать по каталогу Битрикс шесть типов сущностей МойСклад: product, variant, bundle, service, productfolder и specialpricediscount. Веб-хук по любому другому типу — например по партии (consignment), контрагенту (counterparty) или заказу покупателя (customerorder) — модуль обработать не может и не пытается.
Раньше такой веб-хук отбрасывался молча: модуль отвечал МойСклад 200 OK, и событие пропадало. При этом подписку на такую сущность вполне можно было зарегистрировать своим кодом через OnBeforeWebHookOptionsBuild — веб-хуки создавались, приходили и терялись.
Теперь модуль отдаёт такой веб-хук вашему коду событием OnWebhookUnknownEntity и на этом свою часть работы заканчивает.
У события нет параметра ms_element — в отличие от OnWebhookUpdate и OnWebhookSkipped. Модуль не знает, как устроена ваша сущность, и в API за ней не обращается. Вам приходит href; запрос по нему — на вашей стороне.
Что модуль гарантирует к моменту вызова
Событие бросается не на сыром теле запроса. К этой точке модуль уже:
- проверил соль в адресе веб-хука (
webhook_url_salt) сравнением за постоянное время; - убедился, что обмен включён, и выбрал профиль обмена по
profile_id; - нормализовал домен в
meta->href; - проверил
hrefна принадлежность эндпоинту МойСклад — чужой адрес в теле веб-хука до обработчика не доходит; - убедился, что действие входит в
CREATE,UPDATE,DELETE; - отсёк повтор того же события в пределах времени дедупликации (
cache_webhook_time, по умолчанию 5 секунд).
Событие приходит по любому типу вне шести штатных, а не только по вашему. Сюда же попадает всё, что не совпало со списком обслуживаемых типов посимвольно: сравнение строгое, поэтому PRODUCT в другом регистре или product с лишним пробелом — для модуля тоже «чужая сущность». Поломки в этом нет, раньше такие веб-хуки точно так же молча отбрасывались, но обработчик, который не проверил entity строгим сравнением, может принять их за свои.
if ($eventParams['entity'] !== 'consignment') {
return;
}
Когда событие не вызывается
| Случай | Почему |
|---|---|
| Сущность из шести штатных | Она идёт своим маршрутом, событиями OnWebhookUpdate, OnWebhookSkipped и OnBeforeDeleteItem |
Действие вне CREATE / UPDATE / DELETE | Модуль отбрасывает такой веб-хук раньше развилки по типу |
href не принадлежит API МойСклад | Отсекается защитой от подстановки чужого адреса |
Повтор того же события в пределах cache_webhook_time | Дедупликация: второй раз событие не бросается |
Событие не попало в первые webhook_limit_count событий запроса | Модуль обрабатывает только начало пачки. Настройка «Количество событий за раз» на вкладке «Веб-хуки» |
| Обмен выключен или соль не совпала | Приём обрывается до разбора тела |
Исключение из вашего обработчика модуль глушит молча — чтобы одна упавшая сущность не оборвала остальные события пачки. Ни в лог модуля, ни в ответ оно не попадёт. Если вам нужно знать о своих ошибках, ловите их сами и пишите в свой лог.
Отменять модулю на чужой сущности нечего: он с ней и так ничего не делает. EventResult возвращать бессмысленно.
- Описание события
- Пример вызова события
- Примеры использования
Параметры события
| Параметр | Тип | Описание |
|---|---|---|
hook | object | Объект веб-хука от МойСклад целиком, с уже нормализованным meta->href |
entity | string | Тип сущности МойСклад, например consignment, counterparty, customerorder |
action | string | Действие веб-хука: CREATE, UPDATE или DELETE |
href | string | Ссылка на сущность в API МойСклад — то же, что hook->meta->href, строкой |
Возвращаемое значение
Ничего возвращать не нужно — результат не читается.
//Пример кода нужно вставить в файл init.php
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'rbs.moyskladstocks',
'OnWebhookUnknownEntity',
'OnWebhookUnknownEntityHandler'
);
function OnWebhookUnknownEntityHandler(\Bitrix\Main\Event $event)
{
$eventParams = $event->getParameters();
$entity = $eventParams['entity']; // тип сущности МойСклад
$action = $eventParams['action']; // CREATE, UPDATE или DELETE
$href = $eventParams['href']; // ссылка на сущность в API МойСклад
// Отсекайте чужие сущности первой строкой: событие приходит по любому
// типу вне шести штатных, а не только по вашему
if ($entity !== 'consignment') {
return;
}
// Исключение отсюда модуль проглотит молча — ловите сами
try {
if ($action === 'DELETE') {
// Сущности в МойСклад уже нет — ходить за ней бессмысленно,
// работайте по идентификатору из href
YourConsignmentStore::dropByHref($href);
return;
}
// CREATE и UPDATE: объект забираете сами
#WORK_AREA#
} catch (\Throwable $e) {
YourLogger::error('OnWebhookUnknownEntity: ' . $e->getMessage());
}
}
Шаг 1. Зарегистрировать подписку на свою сущность
Пока подписки нет, МойСклад веб-хуки по вашей сущности не шлёт, и событие не сработает. Сущность объявляется через OnBeforeWebHookOptionsBuild — после этого она встаёт на вкладке «Веб-хуки» отдельным блоком наравне с товарами, и подписка создаётся галкой в настройках модуля.
Шаг 2. Забрать сущность из МойСклад по href
function OnWebhookUnknownEntityHandler(\Bitrix\Main\Event $event)
{
$eventParams = $event->getParameters();
if ($eventParams['entity'] !== 'consignment' || $eventParams['action'] === 'DELETE') {
return;
}
try {
// ApiNew::get() принимает как относительный путь ('/entity/consignment/...'),
// так и href целиком. Токен, лимиты запросов и профиль обмена
// он берёт из настроек модуля сам
$msObject = \Rbs\MoyskladStocks\ApiNew::get($eventParams['href']);
if (!is_object($msObject) || \Rbs\MoyskladStocks\Utils::has_errors($msObject)) {
return;
}
YourConsignmentStore::upsert($msObject);
} catch (\Throwable $e) {
YourLogger::error('OnWebhookUnknownEntity: ' . $e->getMessage());
}
}
Шаг 3. Узнать профиль обмена, если их несколько
В параметрах события профиль не передаётся — обработчик один на все профили. Номер активного профиля берётся у модуля:
$profileId = (int)\Rbs\MoyskladStocks\Config::getProfileId();
МойСклад не гарантирует доставку, а модуль обрабатывает только первые webhook_limit_count событий запроса. Стройте на веб-хуках быструю реакцию, а полноту данных обеспечивайте отдельным периодическим проходом.
Обновление на 3.9.0: что делать владельцу существующей доработки
Короткий ответ — ничего. Событие только добавляет то, чего раньше не было; ни одно существующее событие модуля не изменило ни состава параметров, ни момента срабатывания.
| Как устроена ваша доработка | Что делать при обновлении |
|---|---|
Обработчики OnWebhookUpdate, OnWebhookSkipped, OnBeforeDeleteItem, OnBeforeWebHookOptionsBuild и события импорта | Ничего. Они работают по шести штатным сущностям, и их маршрут не изменился |
| Собственный приёмник веб-хуков на своём адресе, мимо модуля | Ничего. Модуль в этой цепочке не участвует |
| Свои сущности ловились «как-нибудь ещё» и не ловились | Появилась штатная дверь — OnWebhookUnknownEntity. Переходить не обязательно, но теперь это единственный поддерживаемый путь |
| В коде модуля правился список обслуживаемых типов | Правка будет затёрта обновлением. Перенесите логику на OnWebhookUnknownEntity: она переживёт все следующие обновления |
В версиях до 3.8.0 веб-хук с действием DELETE по любому типу сущности шёл в штатное удаление: модуль искал в каталоге элемент, у которого XML_ID совпадает с идентификатором из href, и удалял или деактивировал его. Это работало и для сущностей, которые модуль не обслуживает, — и было закрыто в 3.8.0 как уязвимость: тело веб-хука недоверенное, а совпадение идентификатора ничего не подтверждает.
Если ваша доработка опиралась на этот побочный эффект, после обновления он не вернётся — ни в 3.8.0, ни в 3.9.0. Снимать элементы каталога по своей сущности теперь нужно самостоятельно, из обработчика OnWebhookUnknownEntity с действием DELETE.