Перейти до основного вмісту
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`, кожен зі своїми оголошеними параметрами. Коли платформа магазину не та, що очікувалася, або бракує адреси, інструмент явно відмовляє замість того, щоб повертати порожній список, який був би інтерпретований як «у нас немає продукту».

Сполучні слова не псують результат

«Filtru de apă» при буквальному пошуку підбирає все, що містить «de». У доставленому пункті каталогу та в пошуку з голосового віджета ми відсікаємо стоп-слова — `si`, `sau`, `de`, `din`, `la`, `cu`, `pe`, `in`, `pentru` — і, якщо після відсікання не залишається нічого корисного, повертаємося до слів довших за дві літери. Частковий збіг має поріг: продукт потрапляє в результати лише якщо збігається щонайменше з половиною слів, що залишилися.

Коли в магазину немає інтерфейсу, ми читаємо сторінку

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

Свіжість із записаними межами, а не з надією

Каталог тримається в пам’яті 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.

Дані

Чого торкаємося, де воно лежить і скільки лишається

Запитання, які ставить кожен, у кого є відповідальний за захист даних, — поставлені тут раніше, ніж їх поставить він.

Дані каталогу
Назва, короткий опис, поточна ціна та ціна в прайсі, наявність, зображення, категорія та адреса товару — усе це читається з публічного інтерфейсу магазину, який і так публікує їх для будь-якого відвідувача. Ми не копіюємо базу даних магазину і не запитуємо торговельні ключі. Вони залишаються в клієнта; ми читаємо їх на запит.
Де зберігається і як довго
Каталог зберігається в пам’яті процесу 10 хвилин на магазин і зникає після перезапуску — у нас немає його постійної копії. На публічній точці каталогу мережа доставки тримає його ще 5 хвилин свіжим і 10 хвилин як стару версію під час оновлення. Заморожений експорт, коли це єдиний технічний варіант, позначається явно як миттєвий із датою, а не як живе джерело.
Дані розмови та замовлення
Те, що питає клієнт, і те, що він замовляє, проходять через платформу розмови і, де потрібно, до системи обліку магазину. Хто має доступ, як довго це зберігається і що видаляється, визначають на проєкті, з реєстром обробок, написаним до запуску, а не після першого інциденту.
Платіжні облікові дані
Торговельні облікові дані зберігаються в базі даних платформи, окремо для кожного магазину, і читаються лише тоді, коли функцію оплати явно активовано для цього магазину. Вони не потрапляють на публічну сторінку і не проходять через розмову. Налаштування торговельного акаунта здійснює власник акаунта, а не ми.
Чого ми не торкаємося
Ми не обробляємо дані карток. Ми не просимо адміністраторський доступ до магазину для читання каталогу — інтерфейс, який використовується, є публічним. Коли проєкт справді потребує привілейованого доступу, його запитують окремо, із письмовою метою, і не тримають «про всяк випадок».

Випадок

Каталог із 154 продуктів, прочитаний у момент запиту

Ситуація

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

Що ми збудували

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

Що вийшло

Перевірено 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 з них — це код і файли з наших репозиторіїв. Ми не публікуємо їхні назви чи номери рядків: разом, на одній сторінці, це надто точно описало б, як побудовані системи, які належать не лише нам. Ми проходимо їх разом із вами, у репозиторії, за запитом — перевірка залишається можливою, просто вона відбувається в розмові.

Що вам хотілося б налагодити?

Розкажіть про свій процес. Разом визначимо, що варто збудувати, що можна підключити і як перевіримо результат.

Поговорімо