29 KiB
Полная документация 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 | да | Процент скидки (0–100) |
remainings |
string | нет | Уровень остатка: high, medium, low, out |
rating |
number | да | Средний рейтинг (0–5) |
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
-
Категории и товары: Если для запрошенного языка есть поле
translations, вернуть переведённыеname,descriptionи т.д. ИЛИ бэкенд может применять переводы на стороне сервера и возвращать уже переведённые поля. -
Поле
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" } } } -
Рекомендуемый подход: Читать заголовок
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.ruhttps://novo.markethttp://localhost:4200(dev)http://localhost:4201(dev, Novo)
11. Настройка Telegram-бота
Каждому бренду нужен свой бот:
- Dexar:
@dexarmarket_bot - Novo:
@novomarket_bot
Бот должен:
- Слушать команду
/start auth_{callbackUrl} - Извлечь callback URL
- Отправить данные пользователя (
id,first_name,usernameи т.д.) на этот callback URL - 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 сессии (если есть) для привязки к заказу, но не требуют авторизации строго. Фронтенд проверяет авторизацию перед оформлением заказа.