Skip to content

RFC: Headless Commerce API for MiniShop3 #577

Description

@Ibochkarev

Описание функции

Архитектурный RFC: как довести существующий Web API (api.php/api/v1) до coherent headless-контракта для Nuxt без rewrite cart/order/customer и без дублирования уже открытых issues #563#576.

Этот issue не заменяет child-issues. Он фиксирует цель, карту зависимостей, что в core / addon, минимальный MVP и roadmap P0–P2.

Проблема, которую решает

Аудит дал пачку точечных issues. Без RFC легко:

Goal

Production Nuxt storefront на инкрементальных добавлениях к /api/v1:

  • один envelope (success / message / data + честный HTTP status);
  • opaque customer token (Bearer + httpOnly cookie);
  • публичный каталог + checkout discovery (category / delivery / payment);
  • cart → order draft → cost → submit → cabinet orders;
  • совместимость с текущими клиентами (Fenom/ApiClient.js).

Не цели RFC: GraphQL, JWT, отдельный BFF в ядре, Manager API для витрины.

Current API

Источник истины: config/routes/web.php.

Area Routes (есть) Готовность для Nuxt
Middleware CORS, RateLimit, ServiceCheck на /api/v1; Token на cart/order/cabinet Работает; hardening #576
Product GET /product/list, GET /product/get/{id} PLP/PDP базово; нет category tree, facets, images[], seo
Cart add/remove/change/change-option/get/clean Достаточно; контракт map vs [] — #570
Order (draft) get/add/set/remove/submit/clean, cost*, address/*, delivery validation Checkout есть; нет public delivery/payment list
Customer auth login/register/logout/forgot/reset, token/get, email verify Ок; нет /me#571
Cabinet profile, addresses CRUD, orders list/get/cancel Ок при customer_id > 0
Health GET /health Ок
Category / Filters / Facets / Delivery list / Payment list Gaps #563#565, #568#569

Уже достаточно для «гостевой корзина + checkout с известными delivery_id/payment_id» (как Fenom). Недостаточно для навигации каталога и выбора доставки/оплаты без хардкода ID.

Proposed API

Инкремент к /api/v1, те же middleware и Response.

Новые публичные (без TokenMiddleware, как product):

Новые/уточнённые auth:

Без смены URL существующих cart/order методов. Нормализация формы cart/get (#570) и HTTP envelope (#572) — additive / bugfix где status теряется.

Architecture

Nuxt (SSR/SPA)
  └─ HTTPS → assets/.../api.php?route=/api/v1/...
       ├─ CorsMiddleware → RateLimitMiddleware → ServiceCheckMiddleware
       ├─ public: product, category*, delivery/list*, payment/list*, health, token/get, auth POST
       └─ TokenMiddleware: cart, order draft, cabinet
            └─ Controllers\Api\Web → Services (ProductCatalog, Cart, Order, Customer*)

Правила:

  1. Витрина не ходит в /api/mgr/.
  2. Кастом и аддоны: ms3_routes_web.custom.php / ms3.routes.d/web/*.php ([Bug] Api\Index грузит web-роуты через connector без mgr Auth на /api/v1 #384).
  3. Плагины msOnGetProductPrice|Fields остаются; list-perf не отключает их молча ([Feature] Web API: производительность ProductCatalogService (N+1 Data, COUNT, hooks) #575).
  4. Один JSON envelope; клиент смотрит HTTP status + success ([Feature] Web API: единый контракт ошибок и HTTP-статусов для TypeScript/Nuxt #572).

Endpoints

Уже в core (сохранить)

GET  /api/v1/product/list|get/{id}
POST /api/v1/cart/*
GET  /api/v1/cart/get
*    /api/v1/order/*          (draft + submit + cost)
POST /api/v1/customer/login|register|logout|forgot-password|reset-password
GET  /api/v1/customer/token/get
*    /api/v1/customer/addresses*
*    /api/v1/customer/orders*
GET  /api/v1/health

Добавить в core (child issues)

GET  /api/v1/category/list
GET  /api/v1/category/get/{id}
GET  /api/v1/category/tree
GET  /api/v1/product/filters          # facets
GET  /api/v1/delivery/list
GET  /api/v1/payment/list?delivery_id=
GET  /api/v1/customer/me

Расширения query product/list и полей get — в #564/#566/#567, не новые path без нужды.

Authentication

Текущее: opaque msCustomerToken; Bearer → MS3TOKEN → cookie httpOnly; guest auto-mint на cart/order.

RFC требует:

Catalog

База: ProductCatalogService + ProductController.

P0: list/get + category (#563) + perf list (#575) до тяжёлого трафика.
P1: filters (#564), images (#566).
P2: deep search / synonyms → addon (не Elasticsearch в ядре).

Categories

#563 — list / get / tree. Только published msCategory. Nested product listing через parent + позже member scope в #564.

Зависимость: SEO category (#567) после get category.

Filters

#564 — query params на product/list (price range, vendor, options, nested/members) совместимо с текущими parent|category, query, sort, limit.

Не ломать default без новых params.

Facets

#565 — отдельный GET /product/filters (counts). Делить с #564 общую модель «filterable fields», не дублировать два диалекта option keys.

Зависимость: семантика фильтров #564 (хотя бы черновик контракта) до или вместе с facets.

Merge recommendation: не закрывать issues в один PR обязательно; связать cross-link и общий lexicon ключей. Дубликатами не считать.

Cart

Существующий API достаточен. #570 — стабилизировать cart object vs array + totals в status. Breaking только если кто-то парсил пустую корзину как [] vs {} — задокументировать / version soft.

Checkout

Поток уже есть: address/set → set delivery_id/payment_id → cost → submit.

Блокер UX: #568 + #569 (discovery). Рекомендация merge в один milestone/PR-пакет «checkout discovery» (два route, один сервис pairing как mgr #374).

Delivery

Сейчас: validation-rules / required-fields при выбранной доставке.
Нужно: #568 delivery/list (active + цена/правила кратко).

Payment

Сейчас: set payment_id вслепую.
Нужно: #569 payment/list?delivery_id= с member filter.

Customers

Auth + addresses + profile уже в core. #571 /me. Email verify оставить.

Wishlist / loyalty / social login → addons.

Orders

Cabinet list/get/cancel scoped by customer_id — OK.
Submit redirect payment — сохранить.
Programmatic mgr orders (#507) — другой вход, не витрина.

SEO

#567 — derived seo block на product/category get. Не отдельный /seo resource server в MVP.

Зависимости: product get есть; category get из #563.

Error contract

#572 — HTTP status через Router::send; стабильные code / errors; не терять status в getData() питомниках.

Делать рано (P0): иначе #573 и Nuxt types плывут.

Security

#576 — P0 до production CORS headless.
Auth processor RateLimiter (login/register/forgot) уже есть — не дублировать в RFC как gap.

Performance

#575 — N+1 Data, COUNT, hooks на list. P0/P1 до marketing traffic.
Facets (#565) проектировать batch SQL сразу, не N+1 counts.

Backward compatibility

Testing strategy

#574 — один Integration WebApi journey (Router boundary).
Плюс unit на каждый child (#575 query count, #576 RL/CORS, #572 status).

Порядок: security + envelope tests раньше полного journey; journey зеленеет по мере появления delivery/payment list (fixture IDs до #568/569).

Documentation

#573 — OpenAPI-ish / TS types от фактического web.php + lexicon errors.
Писать после #572 и P0 endpoints; обновлять тем же PR что route.

Existing issues map

Issue Topic Action
#563 Category API Keep; P0
#564 product/list filters Keep; P1; link #565
#565 product/filters facets Keep; P1; depends contract #564
#566 images[] Keep; P1
#567 seo block Keep; P1; depends #563 for category
#568 delivery/list Keep; P0; pair with #569
#569 payment/list Keep; P0; pair with #568
#570 cart response Keep; P1 (MVP can ship with known quirk)
#571 auth /me Keep; P0 for SSR cabinet
#572 error/HTTP envelope Keep; P0 foundation
#573 docs Keep; P1 after P0 APIs
#574 journey tests Keep; P0 skeleton → expand
#575 catalog perf Keep; P0/P1
#576 security hardening Keep; P0 first
#541 cart toast locale Orthogonal (web ctx); not headless RFC

Duplicates

Явных дублей среди #563#576 нет. Не закрывать одно как dup другого.

Should merge (implementation packaging, not GitHub close)

  1. [Feature] Web API: публичный список методов доставки (delivery/list) #568 + [Feature] Web API: публичный список методов оплаты (payment/list) #569 — один PR «checkout discovery».
  2. [Feature] Web API: расширенные фильтры product/list для headless #564 + [Feature] Web API: product/filters — facets для headless PLP #565 — общая filter key schema; два endpoint OK.
  3. [Feature] Web API: единый контракт ошибок и HTTP-статусов для TypeScript/Nuxt #572 + кусок [Feature] Docs: полный справочник Web API для Nuxt/TypeScript (сверка с web.php) #573 — error codes table в docs тем же milestone.

Missing dependencies (зафиксировать в child issues)

#576 ─┬─► production headless
#572 ─┼─► #573, Nuxt client
#563 ─┬─► #567 (category seo)
      └─► Nuxt nav MVP
#568+569 ─► checkout without hardcoded IDs
#564 ─► #565 (facet field parity)
#575 ─► before heavy #564/#565 traffic
#574 ─► tracks P0 journeys; expands with new routes

Over-engineered (out of RFC / addon)

  • JWT / OAuth2 в core
  • Elasticsearch / Algolia в core
  • GraphQL gateway
  • Переписывание cart/order URL под REST resources (/carts/{id})
  • Отдельный SEO microservice
  • Manager endpoints для витрины

Core vs addon

In MiniShop3 core Addon / custom routes
Category, delivery/payment list, list filters, facets, images[], seo DTO, /me, envelope, RL/CORS harden, catalog perf, journey tests, Web API docs Full-text search engine, recommendations, wishlist, reviews, loyalty, marketplace multi-vendor UI, payment gateway redirect plugins (уже паттерн payment class), ERP sync

Кастом магазинов: core/config/ms3.routes.d/web/*.php.

Minimal API for production Nuxt storefront

MVP (можно открыть магазин с простым PLP):

  1. product list/get
  2. category tree/list/get
  3. cart + order cost/submit
  4. delivery/list + payment/list
  5. token guest + login/register (optional cabinet)
  6. [Bug] Web API: hardening rate-limit key, CORS wildcard match, token in query #576 security + [Feature] Web API: единый контракт ошибок и HTTP-статусов для TypeScript/Nuxt #572 errors
  7. [Feature] Web API: производительность ProductCatalogService (N+1 Data, COUNT, hooks) #575 list perf acceptable

Ещё не MVP, но production polish: facets, images[], seo, /me, cart shape, docs, full journey CI.

Implementation roadmap

P0 — required for Nuxt MVP

  1. [Bug] Web API: hardening rate-limit key, CORS wildcard match, token in query #576 security hardening
  2. [Feature] Web API: единый контракт ошибок и HTTP-статусов для TypeScript/Nuxt #572 error / HTTP contract
  3. [Feature] Web API: публичный Category API для headless (list / get / tree) #563 Category API
  4. [Feature] Web API: публичный список методов доставки (delivery/list) #568 + [Feature] Web API: публичный список методов оплаты (payment/list) #569 delivery/payment list (один пакет)
  5. [Feature] Web API: производительность ProductCatalogService (N+1 Data, COUNT, hooks) #575 catalog list perf (хотя бы Data prefetch)
  6. [Feature] Web API: интеграционный test suite для headless Nuxt journey #574 journey suite skeleton (fixture IDs → потом list endpoints)
  7. [Feature] Web API: контракт customer auth для Nuxt SSR (me / TTL / cookie+Bearer) #571 /me + TTL docs (если SSR cabinet в MVP)

P1 — required for production

  1. [Feature] Web API: расширенные фильтры product/list для headless #564 advanced product/list filters
  2. [Feature] Web API: product/filters — facets для headless PLP #565 facets
  3. [Feature] Web API: галерея изображений товара (images[]) для headless #566 images[]
  4. [Feature] Web API: seo-блок для product/category (headless SSR) #567 seo block
  5. [Feature] Web API: нормализовать контракт ответа корзины (cart/get) для headless #570 cart response normalize
  6. [Feature] Docs: полный справочник Web API для Nuxt/TypeScript (сверка с web.php) #573 Web API docs / TS
  7. [Feature] Web API: интеграционный test suite для headless Nuxt journey #574 полный happy-path + error cases в CI

P2 — future

  1. Nested/member category product scope beyond [Feature] Web API: расширенные фильтры product/list для headless #564
  2. Vendor public DTO expand
  3. Stricter rate limits per-route
  4. Addon: search index, wishlist, reviews
  5. Optional OpenAPI generate from routes

Альтернативные варианты

  1. BFF только на Nuxt server — быстрее старт, каждый магазин изобретает category/delivery. RFC предпочитает core discovery.
  2. Использовать mgr API с service account — security anti-pattern; отклонено.
  3. Большой bang rewrite /api/v2 — без доказанной несовместимости v1; отклонено.

Примеры использования

Nuxt SSR PLP:

GET /api/v1/category/tree
GET /api/v1/product/list?parent=12&limit=24&sort=price&dir=asc
GET /api/v1/product/filters?parent=12   # P1

Checkout:

GET /api/v1/delivery/list
GET /api/v1/payment/list?delivery_id=2
POST /api/v1/order/set { delivery_id, payment_id, ... }
GET /api/v1/order/cost
POST /api/v1/order/submit

Критерии приёмки (для этого RFC)

Дополнительный контекст

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestpriority: highВажно исправить в ближайшее время

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions