Один каталог - все каналы продаж: Rozetka, Prom, Epicentr, Google и KeyCRM из одного хаба
Магазин, который продаёт ещё на трёх маркетплейсах и в Google, держит пять копий каталога, и каждое изменение цены должно дойти до всех. Хаб Ezeden хранит один каталог и сам говорит на языке каждого канала - и никогда не удаляет и не пересоздаёт объявление, которое уже продаёт.
Задача
Ezeden продаёт на собственной витрине и ещё на четырёх площадках: на украинских маркетплейсах Rozetka, Prom.ua и Epicentr и в Google Merchant Center, откуда товары бесплатно попадают в поиск и “Покупки” Google. На маркетплейсах уже были тысячи объявлений магазина, созданных вручную за годы, и каждая площадка хочет видеть каталог по-своему. Заказы обрабатываются в KeyCRM.
Вести пять копий каталога вручную - значит отстающие цены, остатки, проданные дважды, и объявления, которые расходятся между собой. Цель - один каталог в хабе, из которого питаются все каналы, причём без удаления и пересоздания чего-либо на маркетплейсах.
Каждому маркетплейсу - его язык
Маркетплейс принимает объявление только в своих идентификаторах. Каждую ночь хаб загружает категории, характеристики и допустимые значения каждой площадки и хранит их по версиям: менеджер видит, что именно площадка добавила, переименовала или убрала.
Сопоставление переводит каталог в эти идентификаторы на трёх уровнях: категория в категорию, характеристика в характеристику, значение в значение.
Категория может делиться: гранитные мойки уходят в одну категорию Rozetka, стальные - в другую, по значению одной характеристики. Единицы и формат тоже задаются правилами: миллиметры становятся сантиметрами, а правило, которое не может перевести единицу, отклоняется ещё при сохранении.
Одна характеристика может заполнять две: Epicentr просит базовый цвет и цвет производителя, и оба берутся из одного цвета в каталоге. Поле, которое площадка требует, а каталог не содержит, - “для кухни” у каждого кухонного смесителя - задаётся один раз на категорию как постоянное значение.
У каждого товара есть статус готовности для каждой площадки: без категории, с пустой обязательной характеристикой или с несопоставленным значением он не уходит. Если площадка убрала запись, сопоставления с ней попадают в очередь “Сломанные сопоставления”.
ИИ-помощник для сопоставления
Первый проход делает помощник. Он отбирает записи площадки, чьи названия похожи на названия в каталоге, на всех языках каталога. Точное совпадение или соответствие, уже подтверждённое в другом месте того же канала, предлагается как уверенное, и менеджер подтверждает все такие одной кнопкой. Когда названия лишь похожи, ИИ-модель выбирает одно - только из отобранного списка - или отвечает, что подходящего нет. На тестовых справочниках она сопоставляла значения на разных языках: русское “круглая” из каталога - с украинским “Кругла” на Rozetka.
Каждое предложение подтверждается один раз; дальше сопоставление применяется ко всем товарам без повторного обращения к модели.
Связка объявлений, которые уже продают
Объявления на маркетплейсах создавались вручную годами и уже продают. Пересоздать их - значит выбросить эту работу, поэтому хаб следует одному правилу: связывать, а не пересоздавать. Он читает, что продаёт каждая площадка - по API или из выгрузки кабинета продавца, - и сопоставляет каждое объявление с товаром:
- артикул магазина в поле кода продавца, артикул производителя или штрихкод решают дело;
- артикул в названии объявления или коды модели в названии - только предлагают кандидата.
Всё спорное - два ключа указывают на разные товары, два объявления на один товар - ждёт в очереди “На проверку” с объяснением. Массово связываются только уверенные совпадения, а кому удобнее таблица, может выгрузить список, вписать артикулы и загрузить его обратно через те же проверки.
Единственная запись на площадку здесь - на Prom.ua: по команде менеджера хаб проставляет артикул магазина как внешний идентификатор объявления и затем перечитывает площадку, чтобы убедиться, что это произошло.
Цены и остатки: отправлено, принято, показано
После связки объявление получает цену и остаток из правил цен и журнала остатков хаба.
Каждое обновление передаёт абсолютные цифры, а не разницу: “цена X, остаток Y” вместе с номером версии. Поэтому повторная отправка безвредна, а старая, пришедшая с опозданием, отбрасывается.
Изменение уходит на площадки быстро, примерно через 30 секунд, но пачками, поэтому импорт, поменявший пять тысяч остатков, превращается в одно обновление на канал. Проход раз в пять минут подхватывает то, о чём не сообщает ни одно событие, например акцию, которая начинается в полночь.
То, что площадка приняла цену, ещё не значит, что она её показывает. Поэтому для каждого объявления экран показывает три цифры: что хочет хаб, что площадка приняла и что она на самом деле показывает при обратном чтении. Rozetka, например, задерживает на проверку своим менеджером цену, поднятую больше чем в полтора раза или сниженную больше чем вдвое. Запрос при этом проходит успешно, поэтому хаб помечает такие объявления как ожидающие площадку, пока обратное чтение не прояснит картину.
Безопасность заложена в само устройство: нулевая цена не отправляется никогда, каждый остаток проходит через буфер канала, а товару, который нужно снять с продажи, отправляется нулевой остаток. Ничто не удаляет и не скрывает объявление.
Отказы попадают в очередь ошибок со словами самой площадки.
Новые объявления по правилам
Какие товары должна нести площадка, решают правила публикации по бренду, категории или поставщику с условиями: полное сопоставление, наличие, фото. Условия влияют только на создание нового объявления, а правило может ждать менеджера в обе стороны, так что сужение правила никогда тихо не снимает объявления с продажи. Карточки уходят по API, фидом или файлом для загрузки - как поддерживает площадка.
Google Merchant Center
Бесплатные товарные объявления Google показывают товары в поиске и в “Покупках”, каждое со ссылкой на страницу магазина. Хаб собирает фид из витрины: название, описание, фото, цену и наличие самой витрины (Google отклоняет предложение, если цена расходится со страницей), категорию из таксономии Google, бренд, штрихкод или артикул, группу цветов и все характеристики. Товар считается опубликованным, когда Google забрал файл.
Заказы и KeyCRM
Хаб опрашивает каждую площадку на предмет заказов и записывает их так же, как заказы витрины, по ценам площадки: заказ с маркетплейса - это факт, а не запрос. Строка без найденного товара сохраняется, а не отбрасывается, а нехватка остатка уходит в очередь менеджера, не теряя заказ.
Каждый заказ сразу после оформления уходит в KeyCRM; если CRM недоступна, он ждёт в очереди и отправляется повторно сам. Обратно приходят статусы, номера накладных, фискальные чеки и отметки об оплате. Таблица статусов в админке определяет, какой статус CRM означает какой статус хаба, а настройка решает, чей статус главнее. Клиенты тоже синхронизируются, с очередью конфликтов для неоднозначных совпадений.
Здоровье интеграций и осторожный запуск
Экран состояния площадок показывает для каждой, принимает ли она токен, когда каждый обмен последний раз прошёл и сломался и что ждёт своей очереди. Токены проверяются каждые шесть часов, и эта же проверка поддерживает токен Rozetka, которая отключает токен, не использовавшийся 24 часа. “Ключ отклонён” и “площадка не ответила” сообщаются раздельно: только первое требует человека.
Для спокойного запуска каждую запись на площадку можно поставить на подтверждение менеджером, при этом чтение продолжает работать.
Технологии
- Хаб: Laravel 13 + Inertia + React 19 + Ant Design
- Коннекторы: Rozetka, Prom.ua, Epicentr, Google Merchant Center, KeyCRM
- ИИ-сопоставление: Anthropic Claude, выбор только из списка кандидатов по названию
- Фоновые задачи: Laravel Horizon на Redis 7, с ограничением частоты для каждой площадки
- Данные: MySQL 8.4; фиды отдаёт сам хабРезультат
У магазина один каталог. Из него цены, остатки, карточки и заказы попадают на Rozetka, Prom.ua, Epicentr, в Google и KeyCRM, каждая цифра проверяется после отправки, а объявления, которые уже продавали, сохраняют свою историю.



