Перейти к основному содержимому
megapromotingДавайте обсудим

Экспертиза · Онлайн-магазины

Магазин, в котором каталог читается в реальном времени, а не копируется один раз и забывается.

Мы строим магазины и, поверх них, слой, которого обычно не хватает: агент, отвечающий клиенту, читает цену и остаток напрямую из магазина в момент вопроса и отказывается выдумывать непубликованную цену.

Уже построеноSe poate verifica din exterior chiar acum, fără să ne întrebi pe noi. Punctul nostru de catalog livrat unui distribuitor răspunde 200 și spune „154 rezultate”; interfața publică a magazinului lui, interogată direct, întoarce antetul `x-wp-total: 154`. Aceeași cifră, două surse independente, verificat pe 06.09.2026. A doua implementare e integrată în platforma de asistenți: serviciul `storeCatalog.service.js` plus trei unelte pe care agentul le poate chema. Rezerva care trebuie spusă: acest al doilea traseu stă pe o ramură de dezvoltare (`feat/store-catalog-global`, `481cfa0`) și nu e încă unificat în trunchi — deci e livrat pentru clienții pe care i-am conectat, nu pornit implicit pentru toți.

Большинство «интеграций каталога» на самом деле — это экспорт. Кто-то однажды выгружает товары, вставляет их в файл, и с этого момента агент, который разговаривает с клиентом, читает старую фотографию магазина. Это работает безупречно до первого изменения цены или первого закончившегося товара — а потом ошибается уверенно, что хуже, чем молчать.

Мы читаем магазин в момент вопроса. Для магазинов на WooCommerce есть публичный интерфейс — Store API — который возвращает товары, цены и доступность без ключей, без аккаунта, без установленного плагина. Адрес — `/wp-json/wc/store/v1/products`, мы запрашиваем его с 100 товарами на странице, с максимальным временем ожидания 20 секунд, и узнаём из заголовка ответа, сколько страниц есть, вместо того чтобы гадать. Первую страницу берём отдельно, остальные — параллельно.

Часть, которая отделяет правильную интеграцию от той, что только выглядит правильной, — это перевод цены. Store API не возвращает «129,90»; он возвращает строку цифр в младших единицах и, отдельно, сколько десятичных знаков у валюты. Тот, кто по привычке делит на 100, молча ошибается на любой валюте, у которой не две десятичные знака. И ещё важнее: цена ноль не означает бесплатно, это означает не опубликовано. У нас ноль превращается в «цена по запросу — подтверждается коллегами», а агент получает явную инструкцию не выдумывать. Это видно в публичном ответе каталожного пункта, прямо сейчас.

Поверх каталога идут остальные части, каждая со своим реальным состоянием: корзина и отправка заказа в систему управления рестораном или магазина, оплата через интерфейс MAIB для торговцев или через Stripe, восстановление брошенных корзин, отслеживание состояния заказа с синхронизацией каждые 15 минут. Где у нас нет рабочей интеграции — а такие случаи есть — ниже написано точно, какой.

Что входит

Работа, по составляющим

Каталог, читаемый в реальном времени, без ключей и без плагина

Для WooCommerce мы используем Store API, публичный интерфейс магазина: `/wp-json/wc/store/v1/products`, 100 товаров на странице, максимальное время ожидания 20 секунд, собственный идентификатор клиента в заголовке и принятие исключительно кода 200. Количество страниц мы читаем из заголовка `x-wp-totalpages`, а общее количество товаров — из `x-wp-total` — мы не пагинируем, пока не выдадим себя. Первая страница берётся отдельно, остальные — параллельно, с потолком в 30 страниц. Ничего не устанавливается в магазине и нам не дают ключи торговца.

Правильно переведённая цена, а ноль обработан как не опубликовано

Store API возвращает цену как строку в младших единицах, плюс число знаков после запятой для валюты. Мы делим на десять в степени этого числа, а не на фиксированные 100 — иначе любая валюта с другим числом знаков после запятой будет вычислена неверно, и никто этого не заметит. Ноль становится `null`, а в ответе появляется «цена по запросу — подтверждается коллегами», вместе с явной инструкцией, данной агенту, не выдумывать и запросить подтверждение у коллеги. Цена уходит в двух формах сразу: готовый к отображению текст для интерфейса и число для логики на бэкенде.

Три инструмента, которые агент может вызывать

В платформе ассистентов каталог доступен как три встроенных инструмента — поиск по словам, сопоставление по устройству или сценарию использования и получение товара по идентификатору. Агент видит их под названиями `catalog_cauta`, `catalog_potrivire` и `catalog_produs`, каждый со своими объявленными параметрами. Когда платформа магазина не та, что ожидается, или адрес отсутствует, инструмент явно отказывает вместо того, чтобы вернуть пустой список, который был бы истолкован как «у нас нет товара».

Связующие слова не портят результат

«Фильтр для воды» при буквальном поиске совпадает со всем, что содержит «для». В поставленном пункте каталога и в поиске из голосового виджета мы отсекаем список стоп-слов — `si`, `sau`, `de`, `din`, `la`, `cu`, `pe`, `in`, `pentru` — и, если после отсечения не остаётся ничего полезного, возвращаемся к словам длиннее двух букв. Для частичного совпадения есть порог: продукт попадает в результаты только если набирает не менее половины оставшихся слов.

Когда у магазина нет интерфейса, мы читаем страницу

Не у каждого магазина есть Store API. Для остальных у нас есть экстрактор, который открывает страницы списка и читает карточки товаров, с бюджетом, записанным в коде: максимум 20 страниц списка, 200 товаров, 24 страницы деталей, 50.000 символов HTML на страницу, 5 секунд на запрос и общий лимит 45 секунд на всю операцию. Сначала он читает структурированные данные типа товара со страницы, затем карточки, затем метаданные для публикации — именно в таком порядке, потому что первый источник — тот, который магазин написал намеренно.

Свежесть с ограничениями, записанными, а не с надеждой

Каталог хранится в памяти 10 минут на магазин, а одновременные запросы к одному и тому же магазину объединяются в один, чтобы десять клиентов, спрашивающих в один момент, не вызывали десять загрузок. На публичной точке каталога ответ также несёт инструкцию кэша для сети доставки: 5 минут свежий, ещё 10 минут выдаётся старый, пока обновляется в фоне. Число возвращаемых результатов ограничено 20, значение по умолчанию — 6.

Заказ, корзина и связь с системой управления

Корзина, отправка заказа и передача его в систему управления клиента — это отдельные функции, а не форма. Есть также восстановление брошенных корзин, заказ через разговор и отслеживание состояния заказа, с двумя запланированными задачами, которые запускаются каждые 15 минут — одна синхронизирует результат заказов, другая дополняет статусы из системы управления на окне в два дня назад и один день вперёд.

Платежи: что работает и что нет

Работает и есть в коде: интерфейс MAIB для торговцев — токен, затем запрос на платёж, с реквизитами каждого магазина, читаемыми из базы данных, и с явно включённой функцией — и Stripe, с суммой, конвертированной в младшие единицы, и с поступлением, сохранённым в учёте. Что НЕ работает, чтобы не было сюрпризов: Paynet, Netopia и mobilPay не реализованы, и код явно отказывает, когда их запрашивают. Путь Moldindconbank — это каркас, который возвращает ошибку без реквизитов. Мы берём их в проект как работу, а не как существующую интеграцию.

Агент не выдумывает, когда сеть падает

Если запрос к магазину не удаётся, инструмент не возвращает пустой список — он возвращает сообщение, которое говорит агенту не формулировать ответ о каталоге. Разница важна: пустой список переводится как «у нас нет», а ошибочно сказанное «у нас нет» теряет заказ так же надёжно, как неверная цена.

Как это выглядит

Путь, шаг за шагом.

01

Проверка магазина, прежде чем что-либо обещать

Мы опрашиваем публичный интерфейс одним запросом, с максимальным временем ожидания 15 секунд, и читаем из заголовка, сколько у него товаров. По этому ответу мы знаем, возможен ли живой путь, сколько товаров есть и насколько полна информация о цене. Если магазин не отвечает, мы говорим об этом до предложения, а не во время реализации.

02

Подключение каталога и первый проход по ценам

Мы предоставляем подключение, плюс проход по товарам с нулевой ценой или без цены — это почти всегда реальные товары, а не ошибки, и то, как с ними работает агент, — решение магазина, а не наше. Мы также предоставляем точный текст, с которым агент отказывается называть непубликованную цену.

03

Инструменты агента и их ограничения

Мы настраиваем поиск, сопоставление и выдачу товара, каждый с максимальным числом результатов, определённым вместе. Здесь же записываются собственные синонимы магазина — как клиент называет товар по сравнению с тем, как его называет каталог. Без этого шага поиск технически корректен и практически бесполезен.

04

Заказ, оплата и связь с учётом

Они строятся в таком порядке, и каждая часть подключается только после проверки реального доступа к системе на другой стороне. Для платежей мы начинаем только с поставщика, для которого у нас есть рабочая реализация; остальное рассматривается как новая работа, с заранее сказанным риском, а не как галочка в предложении.

05

Передача с перечнем недочётов

Мы передаём адреса, настройки, ограничения, записанные в коде, и список того, что не покрыто. Например: публичная точка каталога, которую мы передали, не имеет максимального времени ожидания для запроса к магазину — если магазин отвечает очень медленно, запрос затягивается. Это реальный недочёт, мы вносим его в список передачи и в план исправлений, а не во внутреннюю заметку.

Un centru. În jur, ce intră în el — pe trasee separate.CENTRUL SE SPRIJINĂ PE CE E ÎN JURUL LUI
Magazinul, agentul care răspunde, sistemul de gestiune și furnizorul de plată — patru sisteme separate, cu o singură sursă de adevăr pentru preț: magazinul, citit în momentul întrebării.

Данные

К чему прикасаемся, где оно лежит и сколько остаётся

Вопросы, которые задаёт всякий, у кого есть ответственный за защиту данных, — заданы здесь раньше, чем их задаст он.

Данные каталога
Название, краткое описание, текущая цена и цена по списку, наличие, изображение, категория и адрес товара — всё читается из публичного интерфейса магазина, который и так публикует это для любого посетителя. Мы не копируем базу данных магазина и не запрашиваем merchant keys. Они остаются у клиента; мы читаем их по запросу.
Где хранится и как долго
Каталог хранится в памяти процесса 10 минут на один магазин и исчезает при перезапуске — у нас нет его постоянной копии. На публичной точке каталога сеть доставки держит его ещё 5 минут свежим и 10 минут как старую версию, пока он обновляется. Замороженный экспорт, когда это единственный технический вариант, явно помечается как мгновенный с датой, а не как живой источник.
Данные разговора и заказа
То, что спрашивает клиент, и то, что он заказывает, проходят через платформу разговора и, где нужно, в систему учёта магазина. Кто имеет доступ, как долго это хранится и что удаляется, определяются по проекту, с реестром обработок, написанным до запуска, а не после первого инцидента.
Платёжные учётные данные
Учётные данные merchant хранятся в базе данных платформы, по одному на магазин, и читаются только когда функция оплаты явно включена для этого магазина. Они не попадают на публичную страницу и не проходят через разговор. Настройка merchant account выполняется владельцем аккаунта, а не нами.
Что мы не затрагиваем
Мы не принимаем данные карт. Мы не запрашиваем администраторский доступ к магазину для чтения каталога — используемый интерфейс публичный. Когда проект действительно требует привилегированного доступа, он запрашивается отдельно, с указанной целью, и не держится «на всякий случай».

Случай

Каталог из 154 товаров, прочитанный в момент вопроса

Ситуация

Дистрибьютор кофе и воды в Кишинёве, с магазином на WooCommerce и агентами, которые отвечают клиентам в тексте и по телефону. Каталог часто меняется; однажды скопированный список через несколько дней становится неверным, а неверная цена, сказанная по телефону, не отзывается.

Что мы построили

Мы установили отдельную точку каталога, которая опрашивает публичный интерфейс магазина: 100 товаров на странице, число страниц читается из заголовка, первая страница берётся отдельно, а остальные — параллельно, предел 30 страниц. Цена переводится из меньших единиц по числу десятичных знаков, объявленному магазином; ноль становится «цена по запросу — подтверждается коллегами», с письменной инструкцией для агента не выдумывать. Поиск отбрасывает служебные слова и требует, чтобы товар совпал как минимум с половиной оставшихся слов, прежде чем появиться в результатах. Ответ хранится 10 минут в памяти и 5 минут в сети доставки, ещё 10 минут выдаётся старым, пока обновляется. Голосовые агенты вызывают его как обычную web-точку, а не как специальную интеграцию.

Что получилось

Проверено 06.09.2026, в двух независимых запросах: точка каталога отвечает «154 результата» и выводит первый товар с ценой и наличием, а второй — с «цена по запросу — подтверждается коллегами»; при прямом запросе публичный интерфейс магазина возвращает `x-wp-total: 154`. Одна и та же цифра из двух источников, которые не знают друг о друге, — это и есть проверка, а не наше заявление.

Чего этот случай не говорит

У поставленной точки каталога нет максимального времени ожидания для запроса к магазину: если магазин отвечает очень медленно, запрос тянется дольше вместо того, чтобы корректно упасть. Это реальный недостаток, выявленный при повторном чтении кода, и он в списке исправлений — не в списке функций. Отдельно: фильтр стоп-слов, описанный выше, активен в поставленной точке и в поиске голосового виджета; в варианте, встроенном в платформу, он ещё не перенесён, поэтому там поиск с большим числом служебных слов даёт более широкие результаты.

Вопросы

О чём нас спрашивают перед тем, как позвонить

Что значит «живой каталог» и как мне проверить, что это не просто слова?

Это значит, что агент читает магазин в момент вопроса. Проверка делается в двух запросах, без нас: наша точка каталога для дистрибьютора отвечает «154 результата», а публичный интерфейс его магазина, опрошенный напрямую, возвращает в заголовке `x-wp-total: 154`. Одна и та же цифра, два независимых источника, в одну и ту же дату. Скопированный однажды каталог так не может — он рассинхронизируется при первом изменении.

Нужно ли мне давать вам ключи или доступ к магазину?

Для чтения каталога на WooCommerce — нет. Store API — это публичный интерфейс магазина: те же данные, которые видит любой посетитель, отданные в машинно-читаемом формате, без ключа продавца и без установленного плагина. Комментарий записан прямо в нашем коде, чтобы не потерялся. Привилегированный доступ мы просим только там, где функция действительно его требует — например, для отправки заказов в систему учёта — и тогда с письменной целью.

Что происходит с товарами без цены?

Они обрабатываются как не опубликованные, а не как бесплатные. В интерфейсе магазина отсутствующая цена приходит как ноль, и наивная интеграция показывает «0 MDL». У нас ноль становится «цена по запросу — подтверждается коллегами», а агент получает явную инструкцию не выдумывать цифру и просить подтверждение у человека. Это видно в публичном ответе: второй товар в списке именно так и отображается.

Насколько свежая цена, конкретно?

Не более 10 минут устаревания на уровне платформы, на один магазин, а одновременные запросы к одному и тому же магазину сводятся в одну загрузку. На публичной точке каталога сеть доставки 5 минут выдаёт свежую версию и ещё 10 минут — старую, пока обновляет её в фоне. Значения настраиваются по проекту; они записаны как переменные, а не скрыты.

Мой магазин не на WooCommerce. Что вы делаете?

Зависит от того, что он отдаёт. Если у него есть собственный интерфейс, мы читаем его как любой другой. Если ничего нет, у нас есть экстрактор, который открывает страницы списков и читает карточки товаров, с лимитом, записанным в коде: 20 страниц, 200 товаров, 5 секунд на запрос, 45 секунд таймаут. Сначала он читает структурированные данные со страницы, затем карточки, затем метаданные для шаринга. Это честный запасной вариант, а не эквивалентный: магазин, который меняет тему, может сломать экстрактор, а интерфейс — нет.

Какие платежи вы можете интегрировать прямо сейчас?

Функционально и в коде: интерфейс MAIB для торговцев — получение токена, затем запрос платежа, при этом учётные данные каждого магазина читаются из базы данных и функция явно активируется — и Stripe, с суммой, переведённой в меньшие единицы, и с поступлением, сохранённым в учёте. Что не функционально, сказано до контракта: Paynet, Netopia и mobilPay не имеют реализации, и код явно отказывает, когда их запрашивают; путь Moldindconbank — это каркас, который возвращает ошибку без учётных данных. Любой из них можно построить, но это уже новая работа со своим риском.

Может ли агент выдумать товар, которого у вас нет?

Может, если вы ему не помешаете — а этим занимаемся мы. Три барьера: неопубликованная цена приходит с письменной инструкцией не выдумывать; неподходящая платформа или отсутствующий адрес заставляют инструмент явно отказаться, а не вернуть пустой список; а при сетевом сбое возвращается сообщение, которое говорит агенту не формулировать ответ о каталоге. Разница между «пустым списком» и «не удалось прочитать» — это разница между потерянным заказом и отложенным одним предложением.

Почему важно, кто делит на 100?

Потому что интерфейс не отправляет «129,90». Он отправляет строку цифр в мелких единицах и, отдельно, сколько десятичных знаков у валюты. Деление на 100 корректно для валют с двумя десятичными знаками и молча неверно для остальных — никто не получает ошибку, только плохие цифры. Мы делим на десять в степени числа, указанного магазином. Это одна строка кода, и именно по таким строкам видно, прочитал ли кто-то документацию или угадал.

Что вы не берёте на себя?

Мы не принимаем данные карты и не настраиваем счёт продавца вместо владельца. Мы не гарантируем, что парсер страницы выдержит смену темы магазина — поэтому мы предпочитаем интерфейс, когда он есть. Мы не обещаем рост продаж; что мы можем показать — что цифра, сказанная агентом, совпадает с цифрой в магазине на ту же дату. И мы не выдаём за интеграцию то, что в коде является скелетом: список выше говорит, что это.

На чём основаны утверждения выше (18 источников)
  1. Punctul de catalog livrat răspunde 200 și spune „Din catalogul <magazin> (154 rezultate, primele 2)”, cu al doilea produs marcat „preț la cerere — se confirmă de colegi”https://coffywater-ai.vercel.app/api/catalog?limit=2&scurt=1 · 2026-09-06
  2. Interfața publică a aceluiași magazin întoarce `x-wp-total: 154` și `x-wp-totalpages: 154` — aceeași cifră ca punctul de catalog, din sursă independentăhttps://coffywater.md/wp-json/wc/store/v1/products?per_page=1 · 2026-09-06

16 из них — код и файлы из наших репозиториев. Мы не публикуем их название и строку: вместе, на одной странице, они слишком точно описали бы, как устроены системы, которые принадлежат не только нам. Мы разбираем их вместе с тобой, в репозитории, по запросу — проверка остаётся возможной, просто она проходит в разговоре.

Что вы хотели бы наладить?

Расскажите о своём процессе. Вместе определим, что стоит построить, что можно подключить и как проверим результат.

Давайте обсудим