Мультивендорный маркетплейс, где несколько продавцов конкурируют на одной карточке товара: витрина, кабинет продавца и админка на общем API, с поиском, доставкой, оплатами и фискальными чеками. Вот решения, которые его сформировали.
Товар — это не предложение
Товар каталога и предложение продавца — разные сущности. Одна карточка описывает саму вещь, а каждый продавец цепляет к ней своё предложение с собственной ценой, остатком, медиа и оптовыми ценовыми уровнями.
Всё сложное в маркетплейсе вытекает из этого разделения: сопоставить входящую позицию с имеющимся товаром, слить дубли, которые проскользнули, и решить, чьё предложение покупатель увидит первым. Сделанное наоборот — по строке товара на каждого продавца — даёт каталог, где та же вещь встречается сорок раз и ни один фильтр этого не исправит.
Поиск, который не отдаёт страницу одному продавцу
Поиск работает на OpenSearch: атрибуты товаров индексируются вложенными документами, поэтому фильтры строятся под категорию, а не под одно поле, а полный путь категории в индексе означает, что фильтр по родительской категории находит всё, что под ней.
Результаты — не плоский список релевантности. Предложения группируются по продавцу, группы упорядочиваются по лучшему предложению и дальше чередуются, так что маркетплейс с одним крупным продавцом и полусотней мелких всё равно показывает мелких. Маркетплейс, где крупнейший продавец забирает каждую страницу выдачи, перестаёт привлекать новых.
Продавцы грузят фиды, а не заполняют таблицы руками
Каталог растёт через пайплайн импорта: сначала сырые позиции, сопоставление полей сохраняется как пресет для каждого продавца, проблемы с медиа фиксируются по позиции, а не валят весь батч, а матчинг предлагает товар каталога, к которому позиция относится.
Новые бренды и категории ждут подтверждения, прежде чем попасть в фасеты поиска. Без этого фильтра один небрежный фид переименовывает целую ветку каталога.
Одиннадцать состояний — и кто их двигал
Заказ проходит путь от созданного через оплаченный, в обработке, готов к отправке, отправлен, доставлен и завершён, а отмена, возврат средств и возврат товара — полноценные состояния, а не флажки.
Каждый переход записывает, что его вызвало и кто его сделал: создание заказа, вебхук оплаты, задача возврата, tracking-push Новой почты, администратор, менеджер продавца или планировщик. Когда покупатель спрашивает, почему в заказе именно такой статус, ответ лежит в строке, а не в логах.
Деньги, доставка и налоговая
Оплаты идут через LiqPay, monobank и Hutko за общим интерфейсом провайдера, поэтому новый эквайр — это новый файл, а не новая ветка в оформлении заказа. Наложенный платёж остаётся таким же способом оплаты, как остальные.
Доставка охватывает Новую почту, Укрпочту, Meest и Delivery Auto — справочники отделений и почтоматов, печать накладных и трекинг, который возвращает статус в заказ. Фискальные чеки идут через Checkbox, Cashalot или «Вчасно», потому что в Украине чек — это не пожелание, а закон.
Сделано, чтобы этим управляли
Чат покупателя с продавцом работает через вебсокеты с Redis под ними. Уведомления, отзывы, жалобы, аудит, CMS-блоки для футера и статических страниц, аналитика продавца и рекламный модуль — отдельные модули на том же API.
Сто три миграции — честная мера маркетплейса: схема движется, потому что бизнес постоянно доузнаёт, что именно он продаёт.
Результат
Работающий мультивендорный маркетплейс на enez.com.ua — витрина покупателя, кабинет продавца и админка на одном API, с фасетным поиском, четырьмя службами доставки, тремя эквайрами и фискальными чеками в продакшене.