# Backend Integration — B2B branch How the `B2B` frontend **sends** and **gets** data, and how that differs from `main`. Scope excludes **login/auth** and **payments** — those contracts are frozen and untouched here. Source of truth in code: - `src/app/services/api.service.ts` — storefront reads/writes - `src/app/core/config/api-config.service.ts` — base-URL resolution - `src/app/interceptors/api-base-url.interceptor.ts` + `api-headers.interceptor.ts` — request rewriting + headers - `src/app/core/config/config.service.ts` + `core/bootstrap/providers/*` — bootstrap load - `src/app/shared/models/config/*` — bootstrap/endpoint models --- ## 1. What changed vs `main` (one screen) | Concern | `main` | `B2B` | |---|---|---| | API base URL | Hardcoded `environment.apiUrl` (`/api`) | Resolved per-tenant at runtime by `ApiConfigService` | | Multi-tenant | One build per brand (`environment.lavero.ts`, `index.novo.html`, …) | **Single build**, tenant resolved from host; per-brand env/html files deleted | | Config source | Compiled into `environment.ts` (brand, theme, logo, phones) | Fetched at runtime via **`GET /bootstrap`** → `BootstrapConfig` | | Data providers | `ApiService` called directly everywhere | Same endpoints, now behind **repository/provider interfaces + DI tokens** (swap mock↔api without touching UI) | | Mock strategy | `mockDataInterceptor` intercepts HTTP | Provider/gateway pattern picks mock vs api per domain (`RuntimeProviderStrategyService`) | | Interceptor chain | `[mockData, apiHeaders, cache]` | `[mockData, **apiBaseUrl**, apiHeaders, adminAuth, cache]` | | Storefront read/write endpoints | see §3 | **unchanged in shape** | **Bottom line for backend:** the storefront read/write contract (categories, items, search, cart, reviews) is the **same as `main`**. What is *new* is (a) a `GET /bootstrap` document the frontend now depends on, and (b) two whole endpoint namespaces (`builder`, `backoffice`) that are declared but **empty** and served by client-side mocks today. --- ## 2. How a request is built (B2B pipeline) Every storefront call goes through this chain: 1. Code calls `this.http.get('/api/category')` (or `${apiConfig.getBaseUrl()}/category`). 2. **`apiBaseUrlInterceptor`** — if the URL starts with `/api`, rewrites it to the resolved tenant base via `ApiConfigService.toApiUrl()`. 3. **`apiHeadersInterceptor`** — on any API request, attaches: - `X-Region` — `Moscow` / `ST. Petersburg` / `Yerevan` (from region id) - `X-Language` — `RU` / `EN` / `AM` - `Currency` — `RUB` (default) / `USD` / `AMD` - `WebSessionID` — 32-char hex; from auth session if present, else a persisted anonymous id (`localStorage: web_session_id`) 4. Request goes out to the resolved absolute base. ### Base-URL resolution (`ApiConfigService.getBaseUrl`) Priority order: 1. localhost → `environment.localhostApiUrl` (`/api`, proxied by dev server) 2. `environment.tenantApiBaseUrls[tenantKey]` (e.g. `default`/`dexarmarket` → `https://api.dexarmarket.ru:445`) 3. `environment.tenantApiTemplate` → `https://{tenant}.api.dexarmarket.ru:445` 4. (opt-in) `bootstrap.apiEndpoints.website.baseUrl` / `bootstrap.tenant.apiBaseUrl` — only if `allowBootstrapApiOverride: true` **and** absolute 5. fallback `environment.apiUrl` > Backend impact: for a **new tenant**, either add it to `tenantApiBaseUrls`, or serve it at `https://.api.dexarmarket.ru:445` so the template matches. No frontend rebuild needed if the template host pattern holds. --- ## 3. GET — reads (storefront, real HTTP today) Identical shapes to `main`. `ApiService` normalizes legacy **and** backOffice response formats (see `normalizeItem`/`normalizeCategory` — dual field names, `0x`-hex colours, `names[]`→translations, `imgs[]`→photos, Go-typo tolerance like `valuue`). | Method | Endpoint | Notes | |---|---|---| | `getCategories()` | `GET /category` | retry ×2 backoff | | `getCategoryItems(id,count,skip)` | `GET /category/:id?count=&skip=` | | | `getItem(id)` | `GET /items/:id` | rating/reviews/questions derived from item | | `searchItems(q,count,skip,opts)` | `GET /searchitems?search=&count=&skip=&categoryIDs=&minPrice=&maxPrice=&tag=&sort=` | returns `{items,total}` | | `getRandomItems(count,category?)` | `GET /items/randomitems?count=&category=` | featured/related | | `ping()` | `GET /ping` | health | `GET /bootstrap` — **new, required in prod.** Returns the full `BootstrapConfig` (tenant, branding, theme, company, featureFlags, `apiEndpoints`, localization, seo, permissions, navigation, footer, pages, staticPages, widgetRegistry). Loaded once, cached (`ConfigService.loadBootstrap`, `shareReplay(1)`). Shape: `src/app/shared/models/config/bootstrap-config.model.ts`; canonical example: `src/assets/mock/bootstrap/bootstrap.json`. On **localhost** (or `useMockData:true`) the `MockBootstrapProvider` serves `bootstrap.json` instead of hitting `/bootstrap`, so the app runs fully offline (`npm run dexar`). --- ## 4. POST — writes (storefront, real HTTP today) Unchanged from `main` (payment writes omitted per scope): | Method | Endpoint | Body | |---|---|---| | `addToCart(sessionId, items)` | `POST /websession/:sessionId` | `[{itemID,quantity,colour?,size?,price?}]` | | `submitReview({...})` | `POST /items/:id/callback` | `{rating,comment,sessionID,timestamp}` | | `submitQuestion({...})` | `POST /items/:id/questiion` | `{question,sessionID,timestamp}` — **note existing path typo, keep it** | | `submitPurchaseEmail({...})` | `POST /purchase-email` | `{email,phone?,telegramUserId,items[]}` | --- ## 5. New backend surface the frontend is waiting on Declared in the bootstrap model (`apiEndpoints.builder`, `apiEndpoints.backoffice`) but currently `{}` empty and served by **localStorage / in-memory gateways**. UI is done; wiring a real API = implement one gateway/provider against the existing interface and rebind its DI token — no page/component changes. ### 5a. Builder (Project Editor) — **highest priority** The editor edits the same `BootstrapConfig` the storefront consumes. Today `ProjectEditorIoService` only `JSON.stringify`s it (export) — **no HTTP, no persistence**. Needed: - `GET /builder/bootstrap/draft` → current draft `BootstrapConfig` - `PUT /builder/bootstrap/draft` ← save draft - `POST /builder/bootstrap/publish` → promote draft to the live `GET /bootstrap` document - `POST /builder/bootstrap/validate` (optional) → **server-side re-validation is mandatory**; the client validator is not a trust boundary ### 5b. Backoffice (admin) — mock gateways awaiting real API Each domain has a `*Gateway`/`*Provider` interface + a `*LocalGateway` mock bound via token. Implement `*ApiGateway` against the same interface, endpoints under `/backoffice/...`, models in `features/admin//models/*.model.ts`: categories, products, orders, transactions, users/roles, dashboard metrics, monitoring feeds, media. Provider selection is centralized in `RuntimeProviderStrategyService` (`getBackofficeProviderMode`, `getProductProviderMode`, `getCategoryProviderMode`): returns `mock` only when `environment.useMockData`, else `api`. > Full per-domain punch list and model shapes: [`BACKEND.md`](BACKEND.md) §5–§16. Handoff framing: [`BACKEND-DIFF-VS-MAIN.md`](BACKEND-DIFF-VS-MAIN.md). --- ## 6. Contract rules for the backend - **Do not change** existing storefront read/write shapes (categories, items, search, cart, reviews, questions) — frontend `main` and `B2B` both depend on them. - **Serve `GET /bootstrap`** matching `bootstrap-config.model.ts`. Field-name tolerance exists on the client, but match the model to avoid normalization surprises. - Respect request headers `X-Region`, `X-Language`, `Currency`, `WebSessionID` for localization/session-scoped responses. - New namespaces `builder/*` and `backoffice/*` are yours to define; keep them consistent with the declared `apiEndpoints` block so they can be published via bootstrap. - **Out of scope here:** auth (Telegram QR session) and payments (`/qr`, `/card`, `/cart` payment) — unchanged, see existing docs. --- ## 7. Known reliability issue (not fixable in frontend) Intermittent `502`/`504` on refresh/back-nav in prod originate from the upstream reverse proxy at `api.dexarmarket.ru:445` / `users.vitanova.network:456` — the frontend calls those absolute URLs directly. Needs DevOps/backend investigation of upstream health/timeouts around bootstrap + session-check.