Магазин в мессенджере — не замена сайту, а отдельная воронка с другой механикой. Пользователь уже залогинен, не нужно регистрироваться, доставка и оплата идут в одном окне. В MAX эта воронка строится на Bot API и инлайн-кнопках, а конверсия упирается в скорость отклика и качество карточек товара.
Каталог и навигация
Каталог в боте редко повторяет каталог сайта один в один. Глубокая иерархия из 4–5 уровней утомляет в чате — лучше плоская витрина с поиском и фильтрами через инлайн-клавиатуру. Карточка товара — это один-два изображения, цена, краткое описание, две кнопки: «В корзину» и «Подробнее». Всё остальное скрывается за разворачиваемыми секциями.
Под капотом каталог обычно лежит в Postgres с индексами по категории и популярности. Кэш горячих позиций уходит в Redis, чтобы не дёргать БД при каждом скролле. Картинки прогоняем через CDN — MAX не любит долгих ответов от бота, и если карточка грузится дольше пары секунд, пользователь уходит.
Mini App vs встроенный inline-каталог
Для каталога есть два архитектурных пути: показать товары прямо в чате через сообщения с инлайн-кнопками или открыть Mini App (WebView со своим UI). Выбор сильно влияет на разработку, конверсию и индексацию.
| Критерий | Inline-каталог | Mini App |
|---|---|---|
| UX навигации | Линейный, одно сообщение = один экран | Привычный SPA, вкладки, фильтры |
| Глубина каталога | До 200–300 SKU комфортно | 10 000+ SKU без проблем |
| Время до первого экрана | 300–600 мс | 1.5–3 с (загрузка WebView) |
| Сложность разработки | 1× | 2.5–3× (фронт + бэк + initData) |
| SEO/индексация | Нет | Нет (закрытая среда мессенджера) |
| Конверсия в чек-аут | 8–14% | 6–11% (просадка на загрузке) |
| Брошенные корзины | Лучше recovery | Хуже видимость через бота |
| Аналитика | События бота | Web-аналитика + события |
Практическое правило: если SKU меньше 300 и каталог плоский — берите inline. Если 1000+ позиций, нужны фасетные фильтры или сравнение товаров — Mini App. Гибрид тоже работает: inline для популярных подборок и быстрых сценариев, Mini App для глубокого поиска.
Модель запасов и резервирование
Самая болезненная ошибка в магазине — продать товар, которого уже нет. Источник правды по остаткам — товароучётная система (МойСклад, 1С), но опираться на её актуальные значения при каждом клике нельзя: API медленный и квотированный. Поэтому остатки кэшируются в Postgres + Redis с инвалидацией по webhook от учётки.
Ключевой паттерн — soft-reserve: при добавлении в корзину или начале чек-аута товар резервируется на N минут (обычно 10–20). Резерв не списывает остаток, но уменьшает «доступно к продаже».
# Псевдокод soft-reserve с защитой от race condition
async def reserve(sku: str, qty: int, user_id: int, ttl_sec: int = 900):
async with db.transaction():
# SELECT FOR UPDATE блокирует строку остатка на время транзакции
stock = await db.fetchrow(
"SELECT available, reserved FROM stock WHERE sku=$1 FOR UPDATE",
sku,
)
free = stock["available"] - stock["reserved"]
if free < qty:
raise OutOfStock(sku, free)
await db.execute(
"UPDATE stock SET reserved = reserved + $1 WHERE sku=$2",
qty, sku,
)
await redis.setex(
f"reserve:{user_id}:{sku}", ttl_sec,
json.dumps({"qty": qty, "ts": time.time()}),
)
Срок резерва истёк — фоновая job снимает его и возвращает товар в продажу. Оплата прошла — резерв конвертируется в фактическое списание. Без SELECT FOR UPDATE или эквивалентного advisory-lock два параллельных запроса увидят одно и то же значение и оба «успешно» зарезервируют последний экземпляр — классический race на чек-ауте Чёрной пятницы.
Корзина, оплата, чек
Корзина живёт в FSM-состоянии пользователя. Хранить её на клиенте нельзя — пользователь может зайти с другого устройства. Используем сессию в Redis с TTL в несколько суток: брошенные корзины потом пригодятся для возврата.
Оплата — это либо платёжная ссылка на эквайринг банка, либо встроенный платёжный шлюз через Bot API, если доступен в MAX. После успешной оплаты бот:
- отправляет чек по 54-ФЗ через ОФД,
- создаёт заказ в учётной системе (1С, МойСклад, retailCRM),
- ставит задачу складу,
- запускает таймер на статусы доставки.
Промокоды, скидки и бонусные программы
Промокоды легко выглядят простыми, пока не появляются комбинированные правила. Минимальный набор типов:
- Процентная скидка —
-15%на корзину или категорию. - Фиксированная сумма —
-500 ₽при заказе от 3000 ₽. - Подарок — бесплатный товар при достижении суммы.
- Бесплатная доставка — снимает стоимость доставки.
- Реферальный — двусторонний (приглашающий получает бонусы, приглашённый — скидку).
Хранение в БД отдельной таблицей с лимитами:
CREATE TABLE promo (
code TEXT PRIMARY KEY,
type TEXT NOT NULL, -- percent | fixed | gift | free_shipping
value NUMERIC NOT NULL,
min_total NUMERIC DEFAULT 0,
valid_from TIMESTAMPTZ,
valid_until TIMESTAMPTZ,
max_uses INTEGER, -- глобальный лимит
max_per_user INTEGER DEFAULT 1,
used_count INTEGER DEFAULT 0,
categories TEXT[], -- ограничение по категориям
is_active BOOLEAN DEFAULT true
);
CREATE TABLE promo_usage (
code TEXT REFERENCES promo(code),
user_id BIGINT,
order_id BIGINT,
used_at TIMESTAMPTZ DEFAULT now(),
PRIMARY KEY (code, user_id, order_id)
);
Проверка промокода — атомарная операция: INSERT ... ON CONFLICT DO NOTHING в promo_usage плюс UPDATE promo SET used_count = used_count + 1 WHERE used_count < max_uses. Если апдейт затронул 0 строк — лимит исчерпан, откатываем. Без этого в момент окончания акции вы выпустите кодов больше, чем планировали.
Бонусные программы (cashback) удобно вести отдельным «кошельком» пользователя: начисление 3–10% от суммы заказа после получения товара (не оплаты — иначе бонусы уйдут на возвраты), списание ограничено долей корзины (обычно до 30%).
Чек-аут и способы оплаты
Чек-аут — это шаги от «Оформить заказ» до «Оплачено». Оптимальная глубина — 3–4 экрана:
- Контакты и доставка — телефон (если ещё не сохранён), регион, способ доставки.
- Адрес или ПВЗ — для курьера полный адрес, для ПВЗ — выбор точки на карте или из списка.
- Оплата — выбор метода, применение промокода/бонусов, итоговая сумма.
- Подтверждение и платёж — переход в платёжную форму банка.
Способы оплаты в России в 2026:
- СБП (QR / по номеру телефона) — комиссия 0.4–0.7%, моментальное зачисление, лучшая конверсия на мобильных.
- Карта (3-D Secure) — комиссия 1.5–2.5%, привычно для аудитории 35+.
- YooMoney / ЮKassa / T-Pay — агрегаторы, удобны для одной интеграции «всё в одном».
- Долями / Подели — BNPL, повышает средний чек на 15–25%, но комиссия 4–6%.
- Наличные/карта при получении — для регионов с низким доверием к онлайну, но конверсия в выкуп падает на 20–40%.
Частичная оплата (предоплата 30% + остаток при получении) реализуется как два отдельных платёжных события на один order_id. Webhook от платёжной системы должен быть идемпотентным: один и тот же payment_id не должен дважды списать товар или дважды отправить чек. Простая защита — уникальный индекс на (payment_id, event_type) в таблице payment_events.
Мульти-регион и доставка
Даже небольшой магазин быстро упирается в региональную специфику: разная стоимость доставки, разные сроки, разный ассортимент.
Регион определяется в три приёма: сохранённое значение из профиля → IP-геолокация (грубо, до города) → явный выбор пользователем. Последнее обязательно — IP врёт у VPN и корпоративных сетей.
Расчёт доставки:
- СДЭК — REST API
/v2/calculator/tariff, возвращает массив тарифов с ценой и сроком. Кэшируем на 1 час по ключу(from_pvz, to_postal, weight, dimensions). - Boxberry — XML API, медленнее СДЭК, но широкая сеть ПВЗ в малых городах.
- Почта России —
tariff.russianpost.ru, нестабильный, лучше дублировать данными из своего справочника. - Самовывоз — фиксированно 0 ₽, требует выбора точки и слота.
- Курьер собственный — простая зональная сетка
(район → цена).
Ограничения по регионам — отдельная таблица region_restrictions с правилами вида «крупногабарит не доставляется в Калининградскую область», «алкоголь не продаётся онлайн нигде». Проверка делается перед чек-аутом, иначе клиент дойдёт до оплаты и получит отказ — резервный сценарий с худшей конверсией.
Статусы заказа и доставка
Самая частая жалоба — «где мой заказ». Бот закрывает её сам: подписка на статусы, кнопка «Отследить» в карточке заказа, push-уведомление при смене статуса. Источников статусов обычно несколько: учётная система генерирует «собран» и «передан в доставку», курьерская служба (СДЭК, Почта России, Boxberry) — этапы маршрута.
Архитектурно это webhook-приёмник на стороне нашего сервиса: каждая внешняя система пушит статус, мы нормализуем его в общий словарь и отправляем сообщение пользователю. Без нормализации в чате будет каша из «processed», «оформлен», «передан в обработку» — пользователь не поймёт.
Возвраты, отказы и претензии
Возврат — это отдельный пользовательский сценарий, который часто откладывают «на потом» и собирают на коленке. По 26-ФЗ ЗоЗПП клиент вправе вернуть непродовольственный товар надлежащего качества в течение 14 дней (для дистанционной торговли — 7 дней с момента получения), а товар ненадлежащего качества — в течение гарантийного срока. Бот должен закрывать оба сценария.
Минимальный флоу:
- В карточке заказа кнопка «Оформить возврат» доступна, если статус =
deliveredи прошло меньше 14 дней. - Пользователь выбирает позиции и причину (брак, не подошёл размер, не соответствует описанию, передумал).
- Прикладывает фото при браке (опционально, но повышает скорость рассмотрения).
- Получает инструкцию: курьерский забор, ПВЗ, почта.
- После приёмки на складе — возврат денег тем же способом, которым оплачивали (по 161-ФЗ и правилам платёжных систем).
Частичный возврат (один товар из заказа) считается отдельной транзакцией. Сумма к возврату = позиция × цена − пропорциональная скидка − пропорциональная стоимость доставки (если возврат не по вине магазина). Шаблон сообщения клиенту:
Возврат №R-2026-04829 принят.
Товар: Чайник Bosch TWK-3A013
Сумма: 2 850 ₽
Срок зачисления: до 10 рабочих дней на карту *4521.
При вопросах — нажмите «Связаться с менеджером».
Чарджбек (оспаривание операции через банк) — отдельный риск. Если клиент жалуется в банк вместо обращения в магазин, эквайер списывает сумму со счёта продавца и берёт штраф 1500–2500 ₽. Сократить чарджбеки помогает быстрая реакция на запросы возврата (до 48 часов) и понятная контактная кнопка в боте — клиент скорее напишет вам, чем в банк.
Интеграции с товароучёткой
Бот сам по себе не источник правды — он витрина и точка приёма заказа. Реальные остатки, цены, заказы, отгрузки живут в товароучётной системе. Три самые частые в РФ:
МойСклад — REST JSON API (api.moysklad.ru/api/remap/1.2). Лёгкий старт, хорошие webhooks, разумные лимиты (45 запросов/3 секунды). Синхронизация:
- В магазин (бот): товары, остатки, цены, скидки → раз в 5–15 минут или по webhook.
- В МойСклад: заказы покупателей, оплаты, отгрузки → синхронно при создании заказа.
RetailCRM — REST + хорошие триггеры маркетинга. Силён в коммуникациях, посредственный складской контур, поэтому часто ставится поверх 1С. Webhooks нативные, лимиты 300 запросов/минуту.
1С:Розница / 1С:УТ — классика. Обмен через CommerceML 2.x (XML по HTTP), либо через одностраничные REST-обёртки (Хорошо, 1С-Битрикс адаптеры). Грабли:
- CommerceML загружает большой каталог одним архивом — ломает онлайн-обновления.
- Кодировка
windows-1251в старых конфигурациях — обязательно проверять. - Нумерация заказов в 1С идёт с префиксами, легко конфликтует с собственной БД магазина.
- Расхождение справочников единиц измерения (
штvsштукvspcs).
Универсальное правило: всегда есть внутренняя БД заказов в магазине, и обмен с учёткой — асинхронный (очередь + ретраи). Если 1С упала на час, магазин продолжает принимать заказы и копит их в очереди, а синхронизация догоняется потом. Прямой синхронный запрос «создать заказ в 1С при оплате» — гарантированный downtime магазина в момент любой проблемы у бухгалтерии.
Лимиты и нагрузка
Типичная архитектура «Postgres + Redis + Go/Python бот + 1 worker» комфортно держит:
- 5 000–20 000 SKU в Postgres с индексами и
pg_trgmдля поиска по названию. - 300–500 одновременно активных пользователей в боте (RPS до 200 на 2 vCPU / 4 GB).
- 30 000–50 000 заказов в месяц без шардинга и без выноса аналитики.
Когда переходить на отдельную поисковую базу:
- Каталог растёт за 30 000 SKU и фасетный поиск (бренд + категория + цена + наличие) начинает занимать больше 200 мс.
- Появляется опечаточный поиск, морфология, синонимы.
- Нужна ранжировка по поведению (популярность, конверсия карточки).
Кандидаты — Meilisearch (проще в эксплуатации, до 1 млн документов на одной машине), Typesense (быстрее на крупных индексах, лучше с фильтрами), OpenSearch (для тех, кто уже живёт в экосистеме Elastic).
Для нагрузки от 1000+ одновременных пользователей и пиков на распродажах — горизонтальное масштабирование бота (несколько инстансов за общим Redis для FSM), вынос отправки сообщений в очередь (NATS, RabbitMQ), отдельная реплика Postgres под чтение каталога.
Возврат брошенных корзин
Брошенная корзина — корзина, в которой больше суток нет активности и нет оплаты. Простая механика: через 2 часа напомнить, через 24 часа предложить промокод, через 72 часа — снять с актуальных. Текст не должен звучать как давление: «вы оставили в корзине X — оформить можно тут».
Метрика, на которую смотрим, — recovery rate: сколько брошенных корзин возвращается. У качественно настроенного бота это 10–20%, что заметно выше email-рассылок.
Удержание после первой покупки
Боты выигрывают у email на втором цикле. После первой покупки запускаем сценарии:
- Через 3–7 дней — запрос отзыва.
- Через 14–30 дней — рекомендация сопутствующего товара.
- Перед предполагаемым закономерным повторным заказом — напоминание (особенно работает для расходников).
Сегментация по истории покупок строится на стороне CRM, бот выступает каналом доставки. Отписка обязательна — иначе попадаем под 38-ФЗ о рекламе.
Чек-лист готовности магазина к запуску
- Каталог отдаёт карточку быстрее 1–2 секунд (95-й перцентиль).
- Корзина переживает перезапуск бота (Redis с персистом AOF/RDB).
- Soft-reserve остатков с TTL и фоновой очисткой просроченных резервов.
- Идемпотентные webhook оплаты (уникальный индекс по
payment_id). - Чек по 54-ФЗ уходит в ОФД даже при задержке ответа банка.
- Все исходящие сообщения логируются с
message_idдля аналитики. - Промокоды защищены от двойного применения транзакцией с проверкой лимита.
- Возврат оформляется через бот без звонка менеджеру для типовых случаев.
- Расчёт доставки кэшируется и не падает при недоступности API курьера.
- Региональные ограничения проверяются ДО формы оплаты.
- Синхронизация с учёткой асинхронная, с очередью и ретраями.
- Есть ручка для оператора, чтобы написать в чат пользователя.
- Включены метрики: time-to-first-byte бота, recovery rate, conversion checkout.
- Юридические документы на месте: оферта, согласие на ПДн, политика возвратов.
- Нагрузочное тестирование под 3× ожидаемый пик (Black Friday).
Итого
Бот-магазин в MAX закрывает не каталог, а удержание и сервис: корзину, оплату, статусы и повторные продажи. Сильная сторона — единый канал общения без переключений. Узкие места — синхронизация с учётной системой и аккуратная работа с уведомлениями, чтобы не превратить бот в спам. Если вся механика выстроена, конверсия из карточки в оплату стабильно выше, чем у мобильного веба того же магазина.
Частые вопросы
Чем бот-магазин в MAX отличается от сайта интернет-магазина?
Это не замена сайту, а отдельная воронка с другой механикой. Пользователь уже залогинен в мессенджере — не нужна регистрация. Доставка, оплата и статусы идут в одном окне без переключений между приложениями. Сильная сторона — удержание и сервис: корзина, оплата, статусы заказа, повторные продажи. Слабая сторона — глубокий каталог: иерархия из 4–5 уровней утомляет в чате, поэтому каталог делается плоской витриной с поиском и фильтрами через инлайн-клавиатуру, а не повторяет сайт один в один.
Как сделать каталог товаров в боте MAX?
Карточка товара — один-два изображения, цена, краткое описание, две кнопки: «В корзину» и «Подробнее». Всё остальное скрывается за разворачиваемыми секциями, чтобы не перегружать чат. Под капотом каталог в PostgreSQL с индексами по категории и популярности. Кэш горячих позиций в Redis, чтобы не дёргать БД при каждом скролле. Картинки через CDN — MAX не любит долгих ответов, и если карточка грузится дольше пары секунд, пользователь уходит. Глубокая иерархия заменяется поиском и фильтрами.
Где хранится корзина пользователя в боте MAX?
В FSM-состоянии пользователя на стороне сервера, не на клиенте. Клиентское хранилище не подходит, потому что пользователь может зайти с другого устройства и потерять корзину. Используется сессия в Redis с TTL в несколько суток — брошенные корзины потом пригодятся для возврата клиента через напоминания. После оплаты бот отправляет чек по 54-ФЗ через ОФД, создаёт заказ в учётной системе (1С, МойСклад, retailCRM), ставит задачу складу и запускает таймер на статусы доставки.
Как настроить уведомления о статусах заказа в боте?
Через webhook-приёмник на стороне бота. Источников несколько: учётная система генерирует «собран» и «передан в доставку», курьерская служба (СДЭК, Почта России, Boxberry) — этапы маршрута. Все статусы нормализуются в общий словарь, чтобы в чате не было каши из «processed», «оформлен», «передан в обработку» — пользователь не поймёт. У пользователя кнопка «Отследить» в карточке заказа и push-уведомления при смене статуса. Это закрывает самую частую жалобу — «где мой заказ».
Как работает возврат брошенных корзин в боте MAX?
Брошенная корзина — это корзина без активности и оплаты больше суток. Простая механика: через 2 часа напомнить, через 24 часа предложить промокод, через 72 часа — снять с актуальных. Текст не должен звучать как давление: «вы оставили в корзине X — оформить можно тут». Главная метрика — recovery rate (сколько брошенных корзин возвращается). У качественно настроенного бота это 10–20%, что заметно выше email-рассылок благодаря мгновенной доставке и одному окну с оплатой.
Какие сценарии удержания работают в боте после первой покупки?
Три классических. Через 3–7 дней — запрос отзыва (когда впечатление свежее). Через 14–30 дней — рекомендация сопутствующего товара на основе истории. Перед предполагаемым повторным заказом — напоминание (особенно работает для расходников: косметика, корм для животных, фильтры для воды). Сегментация по истории покупок строится на стороне CRM, бот выступает каналом доставки. Отписка от рассылок обязательна — иначе попадаем под 38-ФЗ о рекламе и можем получить претензию от ФАС.
Как защитить промокоды от перерасхода и двойного применения?
Промокоды хранятся в отдельной таблице с лимитами (max_uses глобально, max_per_user на пользователя, valid_from/valid_until по сроку, categories по категориям). Применение — атомарной транзакцией: INSERT в promo_usage с уникальным ключом (code, user_id, order_id) плюс UPDATE promo SET used_count = used_count + 1 WHERE used_count < max_uses. Если апдейт затронул 0 строк — лимит исчерпан, транзакция откатывается. Без этого в момент окончания акции вы выпустите кодов больше, чем планировали, особенно если код вирусится в соцсетях за пару часов до дедлайна.
Как организовать возврат товара через бот в MAX?
В карточке доставленного заказа кнопка «Оформить возврат» доступна 14 дней (по 26-ФЗ ЗоЗПП — 7 дней для дистанционной торговли с момента получения для товара надлежащего качества, и в пределах гарантийного срока для брака). Пользователь выбирает позиции и причину, при браке прикладывает фото. Получает инструкцию по способу возврата (курьерский забор, ПВЗ, почта). После приёмки на складе деньги возвращаются тем же способом, которым оплачивали (по 161-ФЗ). Частичный возврат — отдельная транзакция: сумма равна цене позиции минус пропорциональная скидка и пропорциональная доставка, если возврат не по вине магазина. Быстрая обработка возвратов через бот сокращает чарджбеки, потому что клиент пишет вам, а не в банк.
Как синхронизировать бот-магазин с МойСклад, RetailCRM или 1С?
Главное правило — обмен асинхронный, через очередь с ретраями. У магазина всегда своя БД заказов, а синхронизация с учёткой идёт фоновой задачей. МойСклад — REST JSON API с хорошими webhooks и лимитом 45 запросов/3 секунды, синхронизация остатков и цен раз в 5–15 минут. RetailCRM — REST с лимитом 300/минуту, силён в маркетинговых триггерах. 1С:Розница и 1С:УТ — обмен через CommerceML 2.x (XML по HTTP) или REST-обёртки; типичные грабли — большой архив каталога ломает онлайн-обновления, кодировка windows-1251 в старых конфигурациях, конфликты префиксов нумерации заказов, расхождения справочников единиц измерения. Прямой синхронный «создать заказ в 1С при оплате» — гарантированный downtime магазина при любой проблеме у бухгалтерии.