Заказы сайта в Битрикс24 - вебхук, сопоставление, дубли
Передаём заказы сайта в портал: вебхук, сопоставление полей, поиск дублей, очередь с повторами и возврат статусов.
Механика
Сайт и портал - две разные системы, даже если обе от одного вендора. Общей базы у них нет, и всё, что связывает магазин с отделом продаж, - это вызовы по сети, которые кто-то должен написать и обслуживать.
Самый простой путь связи двух систем - это входящий вебхук самого портала. Портал выдаёт адрес с токеном и набором прав, а сайт зовёт по нему методы работы со сделками и контактами; приложение с авторизацией нужно там, где интеграцию раздают многим клиентам.
Момент передачи заказа в портал выбирают осознанно, а не по удобству кода. Заказ, созданный за секунду и отменённый за минуту, засоряет воронку, поэтому чаще передают оплаченный заказ, а не каждую корзину, доехавшую до кнопки оформления.
Сопоставление полей заказа и сделки делают до первой строки кода. Свойства заказа, тип плательщика, состав корзины и комментарий покупателя должны лечь в поля сделки, и список этих пар согласуют с отделом продаж письменно.
Дубли карточек - главная боль такой интеграции на длинной дистанции. Один и тот же покупатель приходит с разной почтой и разными телефонами, и без поиска существующего контакта портал через месяц наполняется одинаковыми карточками.
Сеть отказывает регулярно, а портал отвечает далеко не всегда и не всегда быстро. Прямой вызов из обработчика заказа теряет данные при первом же сбое, поэтому передачу кладут в очередь и повторяют с паузой, а не «пробуют ещё раз» на месте.
Обратное направление обмена нужно почти всегда, и о нём забывают чаще всего. Менеджер меняет сделку в портале, и сайт должен об этом узнать: статус заказа, отметка оплаты, комментарий - всё это возвращается исходящим вебхуком портала.
Ограничения по частоте вызовов существуют и в коробке, и в облаке. Пачку заказов передают групповым вызовом, а не циклом одиночных запросов, иначе портал начнёт отвечать отказами по превышению частоты.
Шаги
- Завести входящий вебхук портала и выдать ему только нужные права.
- Согласовать сопоставление полей заказа и сделки письменным списком.
- Повесить передачу на событие оплаты заказа, а не на его создание.
- Искать существующий контакт по телефону и почте до создания нового.
- Складывать вызовы в очередь и повторять их с паузой при отказах.
- Принять обратный вебхук портала для возврата статусов на сайт.
Код
Зовём метод портала через вебхук:
use Bitrix\Main\Web\HttpClient;
$http = new HttpClient(['socketTimeout' => 5, 'streamTimeout' => 10]);$response = $http->post($webhookUrl . 'crm.deal.add.json', [ 'fields' => ['TITLE' => 'Заказ №' . $order->getField('ACCOUNT_NUMBER'), 'OPPORTUNITY' => $order->getPrice(), 'CURRENCY_ID' => $order->getCurrency()],]);// адрес вебхука содержит токен: он лежит в настройках, а не в кодеТаймауты обязательны с первого дня. Портал отвечает быстро, пока с ним всё в порядке; в остальное время запрос без таймаута держит процесс сайта до предела времени выполнения.
Ищем существующий контакт до создания:
$found = $http->post($webhookUrl . 'crm.duplicate.findbycomm.json', [ 'entity_type' => 'CONTACT', 'type' => 'PHONE', 'values' => [$phone],]);// нашли - используем найденный идентификатор, не нашли - создаём контакт// поиск по почте делают вторым вызовом: у одного человека их бывает несколькоПоиск дублей стоит одного лишнего вызова и экономит месяцы уборки. Без него отдел продаж через полгода работает с картотекой, в которой один покупатель встречается пять раз.
Сопоставляем свойства заказа с полями сделки:
$map = [ 'DELIVERY_ADDRESS' => 'UF_CRM_ADDRESS', // своё поле сделки в портале 'COMMENT' => 'COMMENTS', 'PERSON_TYPE' => 'UF_CRM_PERSON_TYPE',];foreach ($map as $propCode => $crmField) { $fields[$crmField] = $props[$propCode] ?? '';}// пары полей держат в одном месте: они меняются чаще, чем сам код передачиПередаём состав заказа отдельным вызовом:
$rows = [];foreach ($order->getBasket() as $item) { $rows[] = ['PRODUCT_NAME' => $item->getField('NAME'), 'PRICE' => $item->getPrice(), 'QUANTITY' => $item->getQuantity()];}$http->post($webhookUrl . 'crm.deal.productrows.set.json', [ 'id' => $dealId, 'rows' => $rows,]);Состав сделки задаётся одним вызовом и заменяется целиком. Дописать одну позицию к существующей сделке нельзя: передают весь список заново, иначе в портале останется старый состав.
Кладём передачу в очередь:
QueueTable::add([ 'ENTITY' => 'ORDER', 'ENTITY_ID' => $orderId, 'ACTION' => 'crm.deal.add', 'STATUS' => 'new', 'ATTEMPTS' => 0,]);// обработчик события только ставит задание, а вызывает портал агент// повтор с паузой: 1, 5, 30 минут - и остановка с уведомлениемПрямой вызов из обработчика заказа связывает оформление с доступностью портала. Очередь разрывает эту связь: покупатель оформляет заказ даже тогда, когда портал недоступен или обновляется.
Передаём пачку заказов групповым вызовом:
$commands = [];foreach ($orders as $i => $order) { $commands['deal' . $i] = 'crm.deal.add?' . http_build_query(['fields' => $fields[$i]]);}$http->post($webhookUrl . 'batch.json', ['halt' => 0, 'cmd' => $commands]);// групповой вызов укладывает до полусотни команд в один запросПринимаем обратный вебхук портала:
// /local/api/b24-hook.php - адрес выдают порталу в настройках исходящего вебхука$token = $_REQUEST['auth']['application_token'] ?? '';if (!hash_equals($expectedToken, $token)) { http_response_code(403); return; // чужой запрос дальше не идёт}// дальше по идентификатору сделки находят заказ и меняют его статусОбратный вызов приходит из сети, и проверять его подлинность обязательно. Токен сверяют сравнением с постоянным временем, а не обычным равенством строк: это дешёвая привычка, которая закрывает целый класс атак.
Ограничения
Коробочный портал и облако различаются деталями. Адреса вебхуков, доступные методы и ограничения частоты у них разные, поэтому интеграцию проверяют на той версии, которая стоит у заказчика.
Ограничение частоты вызовов особенно больно бьёт по массовым переносам истории. Первая же выгрузка истории заказов упирается в него, и её делают порциями с паузой, а не одним проходом по всей базе.
Поля сделки меняются без предупреждения со стороны портала. Менеджер добавляет своё поле, переименовывает воронку или меняет стадии, и сопоставление рассыпается молча - до первой сверки.
Дубли контактов не исчезают полностью даже при аккуратном поиске существующих карточек в портале. Поиск по телефону и почте закрывает большинство случаев, но покупатель с новым номером всё равно создаст вторую карточку, и периодическая уборка в портале остаётся ручной работой.
Обратное направление требует своего адреса на сайте, доступного снаружи и защищённого токеном. Он должен быть доступен снаружи, защищён токеном и переживать повторные вызовы: портал повторяет уведомление, если не получил ответа.
Интеграция живёт дольше самого проекта и требует присмотра всё это время, год за годом. Токены протухают, права меняются, портал обновляется - поэтому её ошибки пишут в журнал и раз в месяц смотрят, что накопилось.
Типичные проблемы
В портале появились дубли контактов на одного покупателя.
Контакт создаётся без поиска существующего по телефону и почте. Поиск дублей делают перед созданием карточки, отдельным дешёвым вызовом.
Часть заказов в портал не попала.
Вызов шёл напрямую из обработчика заказа и потерялся при сбое сети. Передачу кладут в очередь с повторами и журналом.
Портал отвечает отказом по превышению частоты.
Заказы передаются циклом одиночных запросов вместо одного группового вызова на всю пачку. Пачку отправляют групповым вызовом с паузой между порциями.
В сделке пропал состав заказа после правки.
Состав задаётся целиком и заменяется при каждом вызове. Передают весь список позиций, а не одну добавленную.
Оформление заказа стало долгим и падает по таймауту.
Обработчик события заказа ждёт ответа портала прямо во время оформления. Вызов выносят в фон, а в обработчике только ставят задание.
Статусы в портале и на сайте разошлись.
Обратное направление не настроено или его адрес недоступен снаружи. Портал возвращает изменения сделки исходящим вебхуком на адрес сайта.
Частые вопросы
Как создавать лид или сделку из заказа магазина?
Обработчиком события заказа, который зовёт метод портала через входящий вебхук. Лид подходит для холодных заявок, сделка - для оформленных заказов, и выбор согласуют с отделом продаж заранее.
Вебхук или приложение?
Для одного проекта достаточно вебхука: он настраивается за минуту и не требует авторизации приложения. Приложение нужно там, где интеграцию ставят многим клиентам и обновляют централизованно.
В какой момент передавать заказ?
Обычно по факту оплаты: неоплаченные заказы засоряют воронку и мешают отделу продаж. Если менеджеры перезванивают по всем заказам, передают при создании, но тогда отменённые заказы закрывают отдельным вызовом.
Как не создавать дубли контактов?
Искать существующий контакт по телефону и почте перед созданием и переиспользовать найденный идентификатор. Полностью дубли это не убирает, но оставляет их единицами вместо сотен.
Что делать, если портал недоступен?
Ничего особенного: задание остаётся в очереди и повторяется с паузой. Именно поэтому передачу не делают прямым вызовом из обработчика оформления заказа.
Смежное
- REST и вебхуки - оглавление подтемы
- Вебхуки и вызовы REST: настройка, права, разбор ошибок - как устроен сам вебхук
- Очередь обмена со сторонней системой: задания, повторы, сверка - механика очереди и повторов
- Вызов внешнего сервиса из кода: таймауты, повторы, журнал - как ходить в чужое API
- Заявки с сайта: хранение, дубли, передача в CRM - то же самое для форм, а не заказов
- Обмен с 1С и HTTP - устройство интеграций целиком