4. API-контракт
API — источник данных для тем и для внешних (headless) клиентов. Объекты в ответах совпадают с объектной моделью.
Модель авторизации
Три типа секретов, три уровня доверия:
| Секрет | Кто держит | Куда ходит |
|---|---|---|
🔑 X-Api-Key (приватный) |
сервер (рантайм витрины / BFF) | в ядро; браузер его НЕ видит |
| 🎫 публичный токен | браузер (встройка виджета/бот) | только в BFF, не в ядро |
| 🍪 cookie | покупатель (браузер) | сессия покупателя в рантайме |
Тема НЕ ходит в ядро напрямую и не знает ключа. Тема (её JS) дергает same-origin эндпоинты своего рантайма → рантайм добавляет X-Api-Key и идёт в ядро. То есть для автора темы API — это /api/... на своём же домене, без ключей.
браузер (тема) ──/api/... (same-origin, без ключа)──► рантайм витрины ──X-Api-Key──► ядро
⚠️ сейчас: часть read-данных отдаётся как проксируемый HTML, часть — как JSON (/api/shop/...). Цель — весь read как чистый JSON/SSR-data, чтобы тема и headless-клиент били по одному контракту.
Read — чтение (для рендера)
Базовый префикс со стороны темы — same-origin /api/shop/… (рантайм подставляет slug тенанта; в примерах ниже {slug} со стороны ядра).
| Метод | Путь | Отдаёт |
|---|---|---|
| GET | /api/shop/{slug} |
shop (брендинг, флаги, категории, доставка, оплата) |
| GET | /api/shop/{slug}/catalog?type=&categoryId=&q=&page=&pageSize= |
список product (карточные поля) + пагинация; варианты схлопнуты |
| GET | /api/shop/{slug}/item/{id} |
полный product (галерея, варианты, состав) |
| GET | /api/shop/{slug}/collections |
подборки (collection[]) |
| GET | /api/shop/{slug}/availability?serviceId=&staffId=&date= |
availability (слоты) ⚠️ сейчас через виджет |
Пагинация: page (1..), pageSize. Поиск: q (по имени/артикулу). Фильтр: type=product|service, categoryId.
Write — действия
| Метод | Путь | Действие |
|---|---|---|
| GET | /api/shop/{slug}/cart |
текущая корзина (cart) |
| POST | /api/shop/{slug}/cart/add |
{itemId, quantity} → добавить |
| PUT | /api/shop/{slug}/cart/item |
{itemId, quantity} → изменить (0 = убрать) |
| POST | /api/shop/{slug}/cart/clear |
очистить |
| POST | /api/shop/{slug}/order |
оформить заказ {contact, delivery, payment, comment} → {orderNumber, token, statusUrl, confirmationUrl?} |
| GET | /api/shop/{slug}/order/{token} |
статус заказа (order) |
| POST | /api/shop/{slug}/order/{token}/pay |
(пере)оплатить онлайн |
| POST | /api/shop/{slug}/hold |
удержать слот ⚠️ |
| POST | /api/shop/{slug}/book |
оформить запись {serviceId, staffId, slot, contact, place} ⚠️ |
Аккаунт покупателя (OTP, единый на все магазины): /api/account/… — вход по коду (SMS/email), история заказов/записей.
Соглашения
- Формат: JSON,
Content-Type: application/json. Поля —camelCase. - Деньги: число в валюте магазина, уже посчитано ядром. Клиент не пересчитывает.
- Ошибки: HTTP-код +
{ "error": "код_или_текст" }. 404 — нет магазина/позиции; 400 — валидация; 401/403 — доступ. - Итоги — только с сервера: сумму корзины/заказа берут из ответа, не считают на клиенте (защита от подделки).
- Идемпотентность заказа:
⚠️планируется ключ идемпотентности, чтобы двойной сабмит не создавал два заказа.
Референс
Машинный контракт — OpenAPI/Swagger ядра (/swagger). Он остаётся источником истины по формам; этот документ — нарратив и «как задумано».
Что дальше
- Как из этих данных и действий собрать тему — 05. Как сделать тему.