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

29 KiB
Raw Blame History

Полная документация Backend API

Последнее обновление: Февраль 2026 Фронтенд: Angular 21 · Два бренда (Dexar + Novo) Охватывает: Каталог, Корзина, Оплата, Отзывы, Регионы, Авторизация, i18n, BackOffice


Базовые URL

Бренд Dev Production
Dexar https://api.dexarmarket.ru:445 https://api.dexarmarket.ru:445
Novo https://api.novo.market:444 https://api.novo.market:444

Глобальные HTTP-заголовки

Фронтенд автоматически добавляет два кастомных заголовка к каждому API-запросу через interceptor. Бэкенд должен читать эти заголовки и использовать для фильтрации/перевода ответов.

Заголовок Пример значения Описание
X-Region moscow ID региона, выбранного пользователем. Отсутствует = все регионы.
X-Language ru Активный язык интерфейса: ru, en или hy.

Поведение бэкенда

  • X-Region: Если присутствует — фильтровать товары/категории только по этому региону. Если отсутствует — возвращать всё (глобальный каталог).
  • X-Language: Если присутствует — возвращать переведённые name, description и т.д., если переводы существуют. Если отсутствует или ru — возвращать на русском (по умолчанию).

Требования CORS для этих заголовков

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

1. Проверка состояния

GET /ping

Простая проверка работоспособности.

Ответ 200:

{ "message": "pong" }

2. Каталог — Категории

GET /category

Возвращает все категории верхнего уровня. Учитывает заголовки X-Region и X-Language.

Ответ 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:

Поле Тип Обязат. Описание
categoryID number да Числовой ID (legacy)
name string да Название категории (переведённое если X-Language)
parentID number да ID родительской категории (0 = верхний уровень)
icon string нет URL иконки категории
wideBanner string нет URL широкого баннера
itemCount number нет Количество товаров в категории
priority number нет Приоритет сортировки (больше = выше)
id string нет Строковый ID из BackOffice
visible boolean нет Видима ли категория (по умолч. true)
img string нет URL изображения из BackOffice (маппится на icon)
projectId string нет Ссылка на проект в BackOffice
subcategories Subcategory[] нет Вложенные подкатегории

Объект Subcategory:

Поле Тип Обязат. Описание
id string да ID подкатегории
name string да Отображаемое название
visible boolean нет Видима ли
priority number нет Приоритет сортировки
img string нет URL изображения
categoryId string да ID родительской категории
parentId string да ID прямого родителя
itemCount number нет Количество товаров
hasItems boolean нет Есть ли товары
subcategories Subcategory[] нет Вложенные дочерние подкатегории

GET /category/:categoryID

Возвращает товары определённой категории. Учитывает заголовки X-Region и X-Language.

Query-параметры:

Параметр Тип По умолч. Описание
count number 50 Товаров на страницу
skip number 0 Смещение для пагинации

Ответ 200: Массив объектов Item.


3. Товары

GET /item/:itemID

Возвращает один товар. Учитывает заголовки X-Region и X-Language.

Ответ 200: Один объект Item.


GET /searchitems

Полнотекстовый поиск по товарам. Учитывает заголовки X-Region и X-Language.

Query-параметры:

Параметр Тип По умолч. Описание
search string Поисковый запрос (обязателен)
count number 50 Товаров на страницу
skip number 0 Смещение для пагинации

Ответ 200:

{
  "items": [ /* объекты Item */ ],
  "total": 128,
  "count": 50,
  "skip": 0
}

GET /randomitems

Возвращает случайные товары для карусели/рекомендаций. Учитывает заголовки X-Region и X-Language.

Query-параметры:

Параметр Тип По умолч. Описание
count number 5 Количество товаров
category number Ограничить данной категорией (опц.)

Ответ 200: Массив объектов Item.


Объект Item

Бэкенд может возвращать товары в любом из двух форматов — legacy или BackOffice. Фронтенд нормализует оба варианта.

{
  "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"
    }
  },
  "comments": [
    {
      "id": "cmt_001",
      "text": "Отличный товар!",
      "author": "user_123",
      "stars": 5,
      "createdAt": "2026-02-01T12:00:00Z"
    }
  ],
  "quantity": 50
}

Все поля Item:

Поле Тип Обязат. Описание
categoryID number да Категория, к которой принадлежит товар
itemID number да Числовой ID товара (legacy)
name string да Название товара
photos Photo[] нет Массив фотографий [{ url }] (legacy)
description string да Текстовое описание
currency string да Код валюты (по умолч. RUB)
price number да Цена
discount number да Процент скидки (0100)
remainings string нет Уровень остатка: high, medium, low, out
rating number да Средний рейтинг (05)
callbacks Review[] нет Отзывы (legacy формат)
questions Question[] нет Вопросы и ответы
id string нет Строковый ID из BackOffice
visible boolean нет Виден ли товар (по умолч. true)
priority number нет Приоритет сортировки (больше = выше)
imgs string[] нет URL картинок из BackOffice (маппится на photos)
tags string[] нет Теги для фильтрации
badges string[] нет Бейджи (bestseller, sale и т.д.)
simpleDescription string нет Краткое текстовое описание
descriptionFields DescriptionField[] нет Структурированное описание [{ key, value }]
subcategoryId string нет Ссылка на подкатегорию из BackOffice
translations Record нет Переводы по ключу языка (см. ниже)
comments Comment[] нет Комментарии в формате BackOffice
quantity number нет Числовое кол-во на складе (маппится на remainings)

Вложенные типы:

Тип Поля
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. Корзина

POST /cart — Добавить товар в корзину

Тело запроса:

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

Ответ 200:

{ "message": "Added to cart" }

PATCH /cart — Обновить количество товара

Тело запроса:

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

Ответ 200:

{ "message": "Updated" }

DELETE /cart — Удалить товары из корзины

Тело запроса: Массив ID товаров

[123, 456]

Ответ 200:

{ "message": "Removed" }

GET /cart — Получить содержимое корзины

Ответ 200: Массив объектов Item (каждый с полем quantity).


5. Оплата (СБП / QR)

POST /cart — Создать платёж (СБП QR)

Примечание: Тот же эндпоинт что и добавление в корзину, но с другой схемой тела запроса.

Тело запроса:

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

Ответ 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 — Проверить статус оплаты

Ответ 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 Значение
CREATED QR создан, не оплачен
WAITING Оплата в процессе
COMPLETED Оплата успешна
EXPIRED QR-код истёк
CANCELLED Оплата отменена

POST /purchase-email — Отправить email после оплаты

Тело запроса:

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

Ответ 200:

{ "message": "Email sent" }

6. Отзывы / Комментарии

POST /comment — Оставить отзыв

Тело запроса:

{
  "itemID": 123,
  "rating": 5,
  "comment": "Отличный товар!",
  "username": "john_doe",
  "userId": 123456789,
  "timestamp": "2026-02-28T12:00:00Z"
}

Ответ 200:

{ "message": "Review submitted" }

7. Регионы

GET /regions — Список доступных регионов

Возвращает регионы, в которых работает маркетплейс.

Ответ 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:

Поле Тип Обязат. Описание
id string да Уникальный идентификатор региона
city string да Название города
country string да Название страны
countryCode string да Код страны ISO 3166-1 alpha-2
timezone string нет Часовой пояс IANA

Фоллбэк: Если эндпоинт недоступен, фронтенд использует 6 захардкоженных значений: Москва, СПб, Ереван, Минск, Алматы, Тбилиси.


8. Авторизация (вход через Telegram)

Авторизация через Telegram с cookie-сессиями (HttpOnly, Secure, SameSite=None).

Все auth-эндпоинты должны поддерживать CORS с credentials: true.

Процесс авторизации

1. Пользователь нажимает «Оформить заказ» → не авторизован → показывается диалог входа
2. Нажимает «Войти через Telegram» → открывается https://t.me/{bot}?start=auth_{callback}
3. Пользователь запускает бота в Telegram
4. Бот отправляет данные пользователя → бэкенд /auth/telegram/callback
5. Бэкенд создаёт сессию → устанавливает Set-Cookie
6. Фронтенд опрашивает GET /auth/session каждые 3 секунды
7. Сессия обнаружена → диалог закрывается → оформление заказа продолжается

GET /auth/session — Проверить текущую сессию

Запрос: Только cookie (сессионная cookie, установленная бэкендом).

Ответ 200 (авторизован):

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

Ответ 200 (сессия истекла):

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

Ответ 401 (нет сессии):

{ "error": "No active session" }

Объект AuthSession:

Поле Тип Обязат. Описание
sessionId string да Уникальный ID сессии
telegramUserId number да ID пользователя в Telegram
username string? нет @username в Telegram (может быть null)
displayName string да Отображаемое имя (имя + фамилия)
active boolean да Действительна ли сессия
expiresAt string да Дата истечения в формате ISO 8601

GET /auth/telegram/callback — Callback авторизации Telegram-бота

Вызывается Telegram-ботом после авторизации пользователя.

Тело запроса (от бота):

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

Ответ: Должен установить cookie сессии и вернуть:

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

Требования к cookie:

Атрибут Значение Примечание
HttpOnly true Недоступна из JavaScript
Secure true Только HTTPS
SameSite None Обязательно для cross-origin (API ≠ фронтенд)
Path /
Max-Age 86400 (24ч) Или по необходимости

POST /auth/logout — Завершить сессию

Запрос: Только cookie, пустое тело {}

Ответ 200:

{ "message": "Logged out" }

Должен очистить/инвалидировать cookie сессии.


Обновление сессии

Фронтенд повторно проверяет сессию за 60 секунд до expiresAt. Если бэкенд поддерживает скользящий срок действия (sliding expiration), можно обновлять Max-Age cookie при каждом вызове GET /auth/session.


9. i18n / Переводы

Фронтенд поддерживает 3 языка: Русский (ru), Английский (en), Армянский (hy).

Активный язык отправляется через HTTP-заголовок X-Language с каждым запросом.

Что бэкенд должен делать с X-Language

  1. Категории и товары: Если для запрошенного языка есть поле translations, вернуть переведённые name, description и т.д. ИЛИ бэкенд может применять переводы на стороне сервера и возвращать уже переведённые поля.

  2. Поле translations на товарах (опциональный подход):

    {
      "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. Рекомендуемый подход: Читать заголовок X-Language и возвращать name/description на этом языке напрямую. Если перевода нет — возвращать русский вариант по умолчанию.


10. Настройка CORS

Для работы auth-cookie и кастомных заголовков конфигурация CORS бэкенда должна включать:

Access-Control-Allow-Origin: https://dexarmarket.ru    (НЕ 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

Важно: Access-Control-Allow-Origin не может быть * при Allow-Credentials: true. Должен быть точный origin фронтенда.

Разрешённые origins:

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

11. Настройка Telegram-бота

Каждому бренду нужен свой бот:

  • Dexar: @dexarmarket_bot
  • Novo: @novomarket_bot

Бот должен:

  1. Слушать команду /start auth_{callbackUrl}
  2. Извлечь callback URL
  3. Отправить данные пользователя (id, first_name, username и т.д.) на этот callback URL
  4. Callback URL: {apiUrl}/auth/telegram/callback

Полный справочник эндпоинтов

Новые эндпоинты

Метод Путь Описание Авторизация
GET /regions Список доступных регионов Нет
GET /auth/session Проверка текущей сессии Cookie
GET /auth/telegram/callback Callback авторизации через бота Нет (бот)
POST /auth/logout Завершение сессии Cookie

Существующие эндпоинты

Метод Путь Описание Авт. Заголовки
GET /ping Проверка состояния Нет
GET /category Список категорий Нет X-Region, X-Language
GET /category/:id Товары категории Нет X-Region, X-Language
GET /item/:id Один товар Нет X-Region, X-Language
GET /searchitems Поиск товаров Нет X-Region, X-Language
GET /randomitems Случайные товары Нет X-Region, X-Language
POST /cart Добавить в корзину / Оплата Нет*
PATCH /cart Обновить кол-во Нет*
DELETE /cart Удалить из корзины Нет*
GET /cart Содержимое корзины Нет*
POST /comment Оставить отзыв Нет
GET /qr/payment/:qrId Статус оплаты Нет
POST /purchase-email Отправить email после оплаты Нет

* Эндпоинты корзины/оплаты могут использовать cookie сессии (если есть) для привязки к заказу, но не требуют авторизации строго. Фронтенд проверяет авторизацию перед оформлением заказа.