Files
marketplaces/docs/API_CHANGES_REQUIRED.md
2026-02-28 17:42:36 +04:00

23 KiB
Raw Permalink Blame History

Complete Backend API Documentation

Last updated: February 2026 Frontend: Angular 21 · Dual-brand (Dexar + Novo) Covers: Catalog, Cart, Payments, Reviews, Regions, Auth, i18n, BackOffice


Base URLs

Brand Dev Production
Dexar https://api.dexarmarket.ru:445 https://api.dexarmarket.ru:445
Novo https://api.novo.market:444 https://api.novo.market:444

Global HTTP Headers

The frontend automatically attaches two custom headers to every API request via an interceptor. The backend should read these headers and use them to filter/translate responses accordingly.

Header Example Value Description
X-Region moscow Region ID selected by the user. Absent = global (all).
X-Language ru Active UI language: ru, en, or hy.

Backend behavior

  • X-Region: If present, filter items/categories to only those available in that region. If absent, return everything (global catalog).
  • X-Language: If present, return translated name, description, etc. for categories/items when translations exist. If absent or ru, use russians defaults.

CORS requirements for these headers

Access-Control-Allow-Headers: Content-Type, X-Region, X-Language

1. Health Check

GET /ping

Simple health check.

Response 200:

{ "message": "pong" }

2. Catalog — Categories

GET /category

Returns all top-level categories. Respects X-Region and X-Language headers.

Response 200:

[
  {
    "categoryID": 1,
    "name": "Электроника",
    "parentID": 0,
    "icon": "https://...",
    "wideBanner": "https://...",
    "itemCount": 42,
    "priority": 10,

    "id": "cat_abc123",
    "visible": true,
    "img": "https://...",
    "projectId": "proj_xyz",
    "subcategories": [
      {
        "id": "sub_001",
        "name": "Смартфоны",
        "visible": true,
        "priority": 5,
        "img": "https://...",
        "categoryId": "cat_abc123",
        "parentId": "cat_abc123",
        "itemCount": 20,
        "hasItems": true,
        "subcategories": []
      }
    ]
  }
]

Category object:

Field Type Required Description
categoryID number yes Legacy numeric ID
name string yes Category display name (translated if X-Language)
parentID number yes Parent category ID (0 = top-level)
icon string no Category icon URL
wideBanner string no Wide banner image URL
itemCount number no Number of items in category
priority number no Sort priority (higher = first)
id string no BackOffice string ID
visible boolean no Whether category is shown (true default)
img string no BackOffice image URL (maps to icon)
projectId string no BackOffice project reference
subcategories Subcategory[] no Nested subcategories

Subcategory object:

Field Type Required Description
id string yes Subcategory ID
name string yes Display name
visible boolean no Whether visible
priority number no Sort priority
img string no Image URL
categoryId string yes Parent category ID
parentId string yes Direct parent ID
itemCount number no Number of items
hasItems boolean no Whether has any items
subcategories Subcategory[] no Nested children

GET /category/:categoryID

Returns items in a specific category. Respects X-Region and X-Language headers.

Query params:

Param Type Default Description
count number 50 Items per page
skip number 0 Offset for paging

Response 200: Array of Item objects.


3. Items

GET /item/:itemID

Returns a single item. Respects X-Region and X-Language headers.

Response 200: A single Item object.


GET /searchitems

Full-text search across items. Respects X-Region and X-Language headers.

Query params:

Param Type Default Description
search string Search query (required)
count number 50 Items per page
skip number 0 Offset for paging

Response 200:

{
  "items": [ /* Item objects */ ],
  "total": 128,
  "count": 50,
  "skip": 0
}

GET /randomitems

Returns random items for carousel/recommendations. Respects X-Region and X-Language headers.

Query params:

Param Type Default Description
count number 5 Number of items to return
category number Optional: limit to this category

Response 200: Array of Item objects.


Item Object

The backend can return items in either legacy format or BackOffice format. The frontend normalizes both.

{
  "categoryID": 1,
  "itemID": 123,
  "name": "iPhone 15 Pro",
  "photos": [{ "url": "https://..." }],
  "description": "Описание товара",
  "currency": "RUB",
  "price": 89990,
  "discount": 10,
  "remainings": "high",
  "rating": 4.5,
  "callbacks": [
    {
      "rating": 5,
      "content": "Отличный товар!",
      "userID": "user_123",
      "timestamp": "2026-02-01T12:00:00Z"
    }
  ],
  "questions": [
    {
      "question": "Есть ли гарантия?",
      "answer": "Да, 12 месяцев",
      "upvotes": 5,
      "downvotes": 0
    }
  ],

  "id": "item_abc123",
  "visible": true,
  "priority": 10,
  "imgs": ["https://img1.jpg", "https://img2.jpg"],
  "tags": ["new", "popular"],
  "badges": ["bestseller", "sale"],
  "simpleDescription": "Краткое описание",
  "descriptionFields": [
    { "key": "Процессор", "value": "A17 Pro" },
    { "key": "Память", "value": "256 GB" }
  ],
  "subcategoryId": "sub_001",
  "translations": {
    "en": {
      "name": "iPhone 15 Pro",
      "simpleDescription": "Short description",
      "description": [
        { "key": "Processor", "value": "A17 Pro" }
      ]
    },
    "hy": {
      "name": "iPhone 15 Pro",
      "simpleDescription": "Կարcheck check check"
    }
  },
  "comments": [
    {
      "id": "cmt_001",
      "text": "Отличный товар!",
      "author": "user_123",
      "stars": 5,
      "createdAt": "2026-02-01T12:00:00Z"
    }
  ],
  "quantity": 50
}

Full Item fields:

Field Type Required Description
categoryID number yes Category this item belongs to
itemID number yes Legacy numeric item ID
name string yes Item display name
photos Photo[] no Legacy photo array [{ url }]
description string yes Text description
currency string yes Currency code (default: RUB)
price number yes Price in the currency's smallest display unit
discount number yes Discount percentage (0100)
remainings string no Stock level: high, medium, low, out
rating number yes Average rating (05)
callbacks Review[] no Legacy reviews (alias for reviews)
questions Question[] no Q&A entries
id string no BackOffice string ID
visible boolean no Whether item is visible (true default)
priority number no Sort priority (higher = first)
imgs string[] no BackOffice image URLs (maps to photos)
tags string[] no Item tags for filtering
badges string[] no Display badges (bestseller, sale, etc.)
simpleDescription string no Short plain-text description
descriptionFields DescriptionField[] no Structured [{ key, value }] descriptions
subcategoryId string no BackOffice subcategory reference
translations Record no Translations keyed by lang code (see below)
comments Comment[] no BackOffice comments format
quantity number no Numeric stock count (maps to remainings on frontend)

Nested types:

Type Fields
Photo url: string, photo?: string, video?: string, type?: string
DescriptionField key: string, value: string
Comment id?: string, text: string, author?: string, stars?: number, createdAt?: string
Review rating?: number, content?: string, userID?: string, answer?: string, timestamp?: string
Question question: string, answer: string, upvotes: number, downvotes: number
ItemTranslation name?: string, simpleDescription?: string, description?: DescriptionField[]

4. Cart

POST /cart — Add item to cart

Request body:

{ "itemID": 123, "quantity": 1 }

Response 200:

{ "message": "Added to cart" }

PATCH /cart — Update item quantity

Request body:

{ "itemID": 123, "quantity": 3 }

Response 200:

{ "message": "Updated" }

DELETE /cart — Remove items from cart

Request body: Array of item IDs

[123, 456]

Response 200:

{ "message": "Removed" }

GET /cart — Get cart contents

Response 200: Array of Item objects (each with quantity field).


5. Payments (SBP / QR)

POST /cart — Create payment (SBP QR)

Note: Same endpoint as add-to-cart but with different body schema.

Request body:

{
  "amount": 89990,
  "currency": "RUB",
  "siteuserID": "tg_123456789",
  "siteorderID": "order_abc123",
  "redirectUrl": "",
  "telegramUsername": "john_doe",
  "items": [
    { "itemID": 123, "price": 89990, "name": "iPhone 15 Pro" }
  ]
}

Response 200:

{
  "qrId": "qr_abc123",
  "qrStatus": "CREATED",
  "qrExpirationDate": "2026-02-28T13:00:00Z",
  "payload": "https://qr.nspk.ru/...",
  "qrUrl": "https://qr.nspk.ru/..."
}

GET /qr/payment/:qrId — Check payment status

Response 200:

{
  "additionalInfo": "",
  "paymentPurpose": "Order #order_abc123",
  "amount": 89990,
  "code": "SUCCESS",
  "createDate": "2026-02-28T12:00:00Z",
  "currency": "RUB",
  "order": "order_abc123",
  "paymentStatus": "COMPLETED",
  "qrId": "qr_abc123",
  "transactionDate": "2026-02-28T12:01:00Z",
  "transactionId": 999,
  "qrExpirationDate": "2026-02-28T13:00:00Z",
  "phoneNumber": "+7XXXXXXXXXX"
}
paymentStatus values Meaning
CREATED QR generated, not paid
WAITING Payment in progress
COMPLETED Payment successful
EXPIRED QR code expired
CANCELLED Payment cancelled

POST /purchase-email — Submit email after payment

Request body:

{
  "email": "user@example.com",
  "telegramUserId": "123456789",
  "items": [
    { "itemID": 123, "name": "iPhone 15 Pro", "price": 89990, "currency": "RUB" }
  ]
}

Response 200:

{ "message": "Email sent" }

6. Reviews / Comments

POST /comment — Submit a review

Request body:

{
  "itemID": 123,
  "rating": 5,
  "comment": "Great product!",
  "username": "john_doe",
  "userId": 123456789,
  "timestamp": "2026-02-28T12:00:00Z"
}

Response 200:

{ "message": "Review submitted" }

7. Regions

GET /regions — List available regions

Returns regions where the marketplace operates.

Response 200:

[
  {
    "id": "moscow",
    "city": "Москва",
    "country": "Россия",
    "countryCode": "RU",
    "timezone": "Europe/Moscow"
  },
  {
    "id": "spb",
    "city": "Санкт-Петербург",
    "country": "Россия",
    "countryCode": "RU",
    "timezone": "Europe/Moscow"
  },
  {
    "id": "yerevan",
    "city": "Ереван",
    "country": "Армения",
    "countryCode": "AM",
    "timezone": "Asia/Yerevan"
  }
]

Region object:

Field Type Required Description
id string yes Unique region identifier
city string yes City name (display)
country string yes Country name
countryCode string yes ISO 3166-1 alpha-2
timezone string no IANA timezone

Fallback: If this endpoint is down, the frontend uses 6 hardcoded defaults: Moscow, SPB, Yerevan, Minsk, Almaty, Tbilisi.


8. Authentication (Telegram Login)

Authentication is Telegram-based with cookie sessions (HttpOnly, Secure, SameSite=None).

All auth endpoints must include withCredentials: true CORS support.

Auth flow

1. User clicks "Checkout" → not authenticated → login dialog shown
2. User clicks "Log in with Telegram" → opens https://t.me/{bot}?start=auth_{callback}
3. User starts the bot in Telegram
4. Bot sends user data → backend /auth/telegram/callback
5. Backend creates session → sets Set-Cookie
6. Frontend polls GET /auth/session every 3s
7. Session detected → dialog closes → checkout proceeds

GET /auth/session — Check current session

Request: Cookies only (session cookie set by backend).

Response 200 (authenticated):

{
  "sessionId": "sess_abc123",
  "telegramUserId": 123456789,
  "username": "john_doe",
  "displayName": "John Doe",
  "active": true,
  "expiresAt": "2026-03-01T12:00:00Z"
}

Response 200 (expired):

{
  "sessionId": "sess_abc123",
  "telegramUserId": 123456789,
  "username": "john_doe",
  "displayName": "John Doe",
  "active": false,
  "expiresAt": "2026-02-27T12:00:00Z"
}

Response 401 (no session):

{ "error": "No active session" }

AuthSession object:

Field Type Required Description
sessionId string yes Unique session ID
telegramUserId number yes Telegram user ID
username string? no Telegram @username (can be null)
displayName string yes User display name (first + last)
active boolean yes Whether session is valid
expiresAt string yes ISO 8601 expiration datetime

GET /auth/telegram/callback — Telegram bot auth callback

Called by the Telegram bot after user authenticates.

Request body (from bot):

{
  "id": 123456789,
  "first_name": "John",
  "last_name": "Doe",
  "username": "john_doe",
  "photo_url": "https://t.me/i/userpic/...",
  "auth_date": 1709100000,
  "hash": "abc123def456..."
}

Response: Must set a session cookie and return:

{
  "sessionId": "sess_abc123",
  "message": "Authenticated successfully"
}

Cookie requirements:

Attribute Value Notes
HttpOnly true Not accessible via JS
Secure true HTTPS only
SameSite None Required for cross-origin (API ≠ frontend)
Path /
Max-Age 86400 (24h) Or as needed

POST /auth/logout — End session

Request: Cookies only, empty body {}

Response 200:

{ "message": "Logged out" }

Must clear/invalidate the session cookie.


Session refresh

The frontend re-checks the session 60 seconds before expiresAt. If the backend supports sliding expiration, it can reset the cookie's Max-Age on each GET /auth/session.


9. i18n / Translations

The frontend supports 3 languages: Russian (ru), English (en), Armenian (hy).

The active language is sent via the X-Language HTTP header on every request.

What the backend should do with X-Language

  1. Categories & items: If translations field exists for the requested language, return the translated name, description, etc. OR the backend can apply translations server-side and return already-translated fields.

  2. The translations field on items (optional approach):

    {
      "translations": {
        "en": {
          "name": "iPhone 15 Pro",
          "simpleDescription": "Short desc in English",
          "description": [{ "key": "Processor", "value": "A17 Pro" }]
        },
        "hy": {
          "name": "iPhone 15 Pro",
          "simpleDescription": "Կarcheck check"
        }
      }
    }
    
  3. Recommended approach: Read X-Language header and return the name/description in that language directly. If no translation exists, return the Russian default.


10. CORS Configuration

For auth cookies and custom headers to work, the backend CORS config must include:

Access-Control-Allow-Origin: https://dexarmarket.ru    (NOT wildcard *)
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, X-Region, X-Language
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS

Important: Access-Control-Allow-Origin cannot be * when Allow-Credentials: true. Must be the exact frontend origin.

Allowed origins:

  • https://dexarmarket.ru
  • https://novo.market
  • http://localhost:4200 (dev)
  • http://localhost:4201 (dev, Novo)

11. Telegram Bot Setup

Each brand needs its own bot:

  • Dexar: @dexarmarket_bot
  • Novo: @novomarket_bot

The bot should:

  1. Listen for /start auth_{callbackUrl} command
  2. Extract the callback URL
  3. Send the user's Telegram data (id, first_name, username, etc.) to that callback URL
  4. The callback URL is {apiUrl}/auth/telegram/callback

Complete Endpoint Reference

New endpoints

Method Path Description Auth
GET /regions List available regions No
GET /auth/session Check current session Cookie
GET /auth/telegram/callback Telegram bot auth callback No (bot)
POST /auth/logout End session Cookie

Existing endpoints

Method Path Description Auth Headers
GET /ping Health check No
GET /category List categories No X-Region, X-Language
GET /category/:id Items in category No X-Region, X-Language
GET /item/:id Single item No X-Region, X-Language
GET /searchitems Search items No X-Region, X-Language
GET /randomitems Random items No X-Region, X-Language
POST /cart Add to cart / Payment No*
PATCH /cart Update cart quantity No*
DELETE /cart Remove from cart No*
GET /cart Get cart contents No*
POST /comment Submit review No
GET /qr/payment/:qrId Check payment status No
POST /purchase-email Submit email after pay No

* Cart/payment endpoints may use the session cookie if available for order association, but don't strictly require auth. The frontend enforces auth before checkout.