Справочник API

Витрина и корзина

запрос каталога витрины
curl -s "https://api.amarix.ru/products?public=true" \
  -H "X-Site-Source: ваш-магазин"

Гостевой режим

Покупателю не нужно входить, чтобы собрать корзину и оформить заказ. Гость опознаётся парой заголовков: идентификатором и его подписью, выданной платформой. Подпись проверяется, поэтому чужую корзину подобрать нельзя.

Когда гость входит в учётную запись, его корзина сливается с той, что была у него раньше.

Какой остаток видит покупатель

В каталоге витрины поле остатка у размера — это доступный остаток: физический минус то, что уже зарезервировано под чужие неоплаченные заказы. Поэтому покупатель не увидит вещь, которая вот-вот уедет к другому.

Публичная часть платформы: каталог, корзина, оформление заказа. Пригодится, если вы делаете свою витрину, мобильное приложение или киоск вместо стандартного сайта. Авторизация покупателя почти нигде не нужна — гость проходит весь путь до оплаты.

GET/products

Каталог витрины

Товары магазина с размерами, ценами и доступными остатками.

Доступ:Без авторизации

Магазин определяется заголовком витрины. Без него платформа не станет гадать и вернёт пустой список — это защита от случайной выдачи чужого каталога.

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringдаКод магазина. Без него ответ будет пустым, хотя код останется успешным.

Параметры запроса

ПараметрТипОбязателенОписание
publictrueнетОтсечь скрытые товары и товары «только по ссылке». Сервер его не требует — без него просто вернёт лишнее, ошибки не будет. Витрина обязана передавать его по смыслу.
categorystring | numberнетЧисло понимается как идентификатор категории, строка — как её точное название.
searchstringнетПоиск по названию и адресному имени.
hasDiscounttrue | falseнетТолько со скидкой либо только без неё.
collectionIdstringнетТовары одной коллекции.
sortBystringнетПорядок. Неизвестное значение молча заменяется порядком по умолчанию.name_asc · name_desc · price_asc · price_desc · newest · oldest
inStocktrue | falseнетСлужебный фильтр по ручному флагу наличия — витрине обычно не нужен.
soldouttrue | falseнетСлужебный фильтр по пометке «продано».
isDeletedtrue | falseнетСлужебный фильтр удалённых товаров.
brandstringнетКод магазина как альтернатива заголовку X-Site-Source. Указанный явно brand имеет приоритет над заголовком.
ответ
[
  {
    "id": "123",
    "slug": "futbolka-basic",
    "name": "Футболка Basic",
    "description": "Хлопок 100%",
    "price": 199000,
    "discount": 10,
    "discountType": "percent",
    "images": ["https://cdn.amarix.ru/products/123/1.jpg"],
    "category": { "id": 5, "name": "Футболки", "slug": "futbolki" },
    "sizes": [
      {
        "id": 45,
        "size": "M",
        "quantity": 12,
        "availableQuantity": 12,
        "reservedQuantity": 0,
        "variantId": "v-1",
        "isVisible": true,
        "salesLimitLeft": 5,
        "price": 199000,
        "discount": 10,
        "discountType": "percent"
      }
    ],
    "productGroups": [
      {
        "id": 7,
        "name": "Цвета",
        "items": [
          { "id": "124", "name": "Футболка Basic (черная)", "slug": "futbolka-basic-black", "image": "https://cdn.amarix.ru/products/124/1.jpg", "label": "Черный" }
        ]
      }
    ],
    "inStock": true,
    "productionTime": 3,
    "assemblyTime": 1,
    "isDeleted": false,
    "brand": "acme",
    "brandId": 1,
    "material": "Хлопок",
    "care": "Стирка 30°",
    "soldout": false,
    "freeShipping": false,
    "salesLimit": 50,
    "isLinkOnly": false,
    "collectionId": 12,
    "sizeGrid": [],
    "badgeText": "Новинка",
    "badgeColor": "#FF0000",
    "popularityRank": 4,
    "displayOrder": 0,
    "imageMetadata": {},
    "relatedProductIds": [125, 126]
  }
]

Ошибки

КодОтвет сервераКогда возникаетЧто делать
200[]Заголовок магазина не передан или код неизвестен.Это не ошибка, а осознанный отказ угадывать. Проверьте заголовок, если список неожиданно пуст.
500{"error":"Internal server error"}Сбой на стороне платформы.—
GET/products/{slug}

Карточка товара

Один товар по адресному имени или числовому идентификатору.

Доступ:Без авторизации

Параметры пути

ПараметрТипОбязателенОписание
slugstring | numberдаАдресное имя товара либо его идентификатор.

Параметры запроса

ПараметрТипОбязателенОписание
brandstringнетКод магазина как альтернатива заголовку X-Site-Source.
allowDeletedtrue | 1нетСлужебный параметр: не скрывать удалённые товары.
ответ
{
  "id": "123",
  "slug": "futbolka-basic",
  "name": "Футболка Basic",
  "description": "Хлопок 100%",
  "price": 199000,
  "discount": 10,
  "discountType": "percent",
  "images": ["https://cdn.amarix.ru/products/123/1.jpg"],
  "category": { "id": 5, "name": "Футболки", "slug": "futbolki" },
  "sizes": [
    {
      "id": 45,
      "size": "M",
      "quantity": 12,
      "availableQuantity": 12,
      "reservedQuantity": 0,
      "variantId": "v-1",
      "isVisible": true,
      "salesLimitLeft": 5,
      "price": 199000,
      "discount": 10,
      "discountType": "percent"
    }
  ],
  "attributes": [],
  "variants": [],
  "productGroups": [
    {
      "id": 7,
      "name": "Цвета",
      "items": [
        { "id": "124", "name": "Футболка Basic (черная)", "slug": "futbolka-basic-black", "image": "https://cdn.amarix.ru/products/124/1.jpg", "label": "Черный" }
      ]
    }
  ],
  "inStock": true,
  "productionTime": 3,
  "assemblyTime": 1,
  "isDeleted": false,
  "brand": "acme",
  "brandId": 1,
  "material": "Хлопок",
  "care": "Стирка 30°",
  "soldout": false,
  "freeShipping": false,
  "salesLimit": 50,
  "isLinkOnly": false,
  "collectionId": 12,
  "sizeGrid": [],
  "badgeText": "Новинка",
  "badgeColor": "#FF0000",
  "displayOrder": 0,
  "imageMetadata": {},
  "relatedProductIds": [125, 126]
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
404{"error":"Товар не найден"}Товара нет, он скрыт, либо принадлежит другому магазину.—
GET/categories

Категории

Разделы каталога с числом товаров.

Доступ:Без авторизации

Параметры запроса

ПараметрТипОбязателенОписание
visibleOnlytrueнетТолько видимые покупателю.
brandIdnumberнетКатегории конкретного магазина.
brandstringнетКод магазина — отдельный от brandId параметр, не то же самое.
ответ
[
  {
    "id": 5,
    "name": "Футболки",
    "slug": "futbolki",
    "moySkladId": null,
    "displayOrder": 0,
    "description": "Мужские и женские футболки.",
    "isVisible": true,
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "deletedAt": null,
    "brands": [{ "id": 1, "code": "acme", "name": "Acme" }],
    "brandIds": [1],
    "productCount": 42
  }
]

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Internal server error"}Сбой на стороне платформы.—
GET/cart

Корзина

Содержимое корзины покупателя или гостя.

Доступ:Гостевая сессияСессия сотрудника

Гостевая корзина живёт по паре заголовков — идентификатору гостя и его подписи. Подпись выдаёт платформа, подделать её нельзя. Когда гость входит в учётную запись, его корзина сливается с пользовательской.

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетКод магазина: корзина у каждого магазина своя. Сервер его не требует — без заголовка тихо подставляется бренд по умолчанию (acme), а не пустой/чужой результат. Для настоящей интеграции передавайте всегда.
X-Guest-IDstringнетИдентификатор гостя, выданный платформой ранее.
X-Guest-TokenstringнетПодпись идентификатора, выданная платформой.
ответ
{
  "id": 987,
  "items": [
    {
      "id": 55,
      "product": {
        "id": "123",
        "name": "Футболка Basic",
        "slug": "futbolka-basic",
        "price": 199000,
        "discount": 10,
        "discountType": "percent",
        "images": ["https://cdn.amarix.ru/products/123/1.jpg"],
        "inStock": true,
        "productionTime": 3,
        "assemblyTime": 1,
        "freeShipping": false,
        "sizes": [
          {
            "id": 45,
            "size": "M",
            "quantity": 12,
            "reservedByOtherOrders": 0,
            "availableQuantity": 12,
            "isVisible": true,
            "price": 199000,
            "discount": 10,
            "discountType": "percent"
          }
        ]
      },
      "size": "M",
      "variantId": null,
      "quantity": 2,
      "total": 358200
    }
  ],
  "total": 358200
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
401{"error":"Invalid guest session"}Подпись гостя не сходится с идентификатором.—
POST/cart/items

Добавить в корзину

Кладёт товар с размером в корзину покупателя или гостя.

Доступ:Гостевая сессияСессия сотрудника

Если такой товар и размер (или вариант) уже в корзине — количество суммируется, отдельной строки не создаётся.

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетКод магазина. Без заголовка тихо подставляется бренд по умолчанию (acme).
X-Guest-IDstringнетИдентификатор гостя. Если отсутствует, сервер сам создаёт гостевую сессию (см. note у GET /cart).
X-Guest-TokenstringнетПодпись идентификатора.

Тело запроса

ПараметрТипОбязателенОписание
productIdnumberдаТовар.
sizeIdnumberнетЛегаси-адресация размера — нужен либо sizeId, либо variantId.
variantIdnumberнетВариант товара (система атрибутов) — альтернатива sizeId, обязателен для товара без легаси-размера (заведённого только на атрибутах вроде Цвета). Нужен либо sizeId, либо variantId.
quantitynumberнетпо умолчанию 1Количество. По умолчанию 1.
ответ
{
  "id": 987,
  "items": [ { "id": 55, "product": { "...": "..." }, "size": "M", "quantity": 2, "total": 358200 } ],
  "total": 358200
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Товар не найден"}productId не существует. Контроллер отдаёт 500, а не 404 — у брошенной ошибки нет статуса, и getPublicErrorMessage в проде подменяет её на общий "Internal server error"; текст из примера виден только вне прод-окружения.—
500{"error":"Этот товар закончился"}Товар помечен soldout. Тот же нюанс со статусом и текстом, что выше.—
500{"error":"Этот товар больше недоступен"}Товар помечен isDeleted.—
500{"error":"Размер не найден"}sizeId не относится к этому товару.—
409{"error":"Basic (M): доступно к продаже только 3 шт."}code: SALES_LIMITЛимит продаж на товар/размер исчерпан или количество его превышает. Единственная ошибка здесь, у которой есть настоящий код статуса — придёт как задокументировано, без подмены на 500.—
PUT/cart/items/{id}

Изменить количество

Меняет количество товара в строке корзины.

Доступ:Гостевая сессияСессия сотрудника

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетКод магазина.
X-Guest-IDstringнетИдентификатор гостя.
X-Guest-TokenstringнетПодпись идентификатора.

Параметры пути

ПараметрТипОбязателенОписание
idintдаИдентификатор строки корзины (CartItem.id, не товара).

Тело запроса

ПараметрТипОбязателенОписание
quantitynumberдаНовое количество. При значении 0 или меньше строка удаляется из корзины вместо ошибки.
ответ
{ "id": 987, "items": [], "total": 0 }

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Корзина не найдена"}У этого гостя/пользователя в этом магазине ещё нет корзины. Статус 500 из-за отсутствия .status у брошенной ошибки — см. note у /cart/items (POST).—
500{"error":"Товар не найден в корзине"}id строки не принадлежит корзине этого покупателя.—
409{"error":"Basic (M): доступно к продаже только 3 шт."}code: SALES_LIMITНовое количество превышает лимит продаж.—
DELETE/cart/items/{id}

Убрать строку из корзины

Удаляет одну позицию из корзины.

Доступ:Гостевая сессияСессия сотрудника

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетКод магазина.
X-Guest-IDstringнетИдентификатор гостя.
X-Guest-TokenstringнетПодпись идентификатора.

Параметры пути

ПараметрТипОбязателенОписание
idintдаИдентификатор строки корзины (CartItem.id).
ответ
{ "id": 987, "items": [], "total": 0 }

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Корзина не найдена"}У этого гостя/пользователя нет корзины в этом магазине. Статус 500 — см. note у /cart/items (POST).—
500{"error":"Товар не найден в корзине"}id строки не принадлежит этой корзине.—
DELETE/cart

Очистить корзину

Удаляет все строки из корзины покупателя или гостя.

Доступ:Гостевая сессияСессия сотрудника

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетКод магазина.
X-Guest-IDstringнетИдентификатор гостя.
X-Guest-TokenstringнетПодпись идентификатора.
POST/orders

Оформление заказа

Превращает корзину в заказ и ставит товар в резерв.

Доступ:Гостевая сессияСессия сотрудника

Собирает заказ из корзины того магазина, с витрины которого пришёл запрос. Итог считается как товары плюс доставка минус промокод. Сразу после создания товар встаёт в резерв на ограниченное время — чтобы его не купил кто-то другой, пока покупатель платит.

Тело запроса

ПараметрТипОбязателенОписание
lastNamestringдаФамилия получателя.
firstNamestringдаИмя получателя.
middleNamestringнетОтчество.
phonestringдаТелефон.
emailstringнетПочта для писем о заказе. Проверка на подтверждённость есть, но обязательность самого поля сервер не проверяет.
telegramstringнетКонтакт в Telegram.
citystringнетГород доставки. Для международной — страна. Сервер не проверяет обязательность — при пустом городе упадёт 500, см. errors.
cityCodenumberнетКод города у перевозчика.
deliveryMethodstringнетпо умолчанию cdek_pvzСпособ доставки. Обязательность не проверяется сервером.cdek_pvz · cdek_courier · international
pvzCodestringнетКод пункта выдачи. Обязателен при доставке в пункт выдачи.
pvzAddressstringнетАдрес пункта выдачи текстом.
streetstringнетУлица. Обязательна при курьерской доставке.
housestringнетДом. Обязателен при курьерской доставке.
apartmentstringнетКвартира.
entrancestringнетПодъезд.
floorstringнетЭтаж.
intercomstringнетДомофон.
postalCodestringнетИндекс.
innstringнетИНН — для покупки от юрлица/ИП.
commentstringнетКомментарий к заказу.
promoCodestringнетПромокод. Неподходящий код не роняет заказ — скидка просто не применится.
paymentMethodstringнетИгнорируется сервером: способ оплаты вычисляется на бэкенде ('manager' для международной доставки, иначе 'tbank') — что бы ни прислал клиент, сохранится это значение.
agreedToTermsbooleanнетСогласие с условиями. Сервер только проверяет истинность значения (!!agreedToTerms), но не то, что поле вообще прислано — по факту необязательное.
agreedToPrivacybooleanнетСогласие на обработку данных. Та же оговорка, что у agreedToTerms.
agreedToMarketingbooleanнетСогласие на рассылку.
marketingUtmCampaignstringнетUTM-метка рекламной кампании, если заказ пришёл по ссылке с меткой.
ответ
{
  "id": 501,
  "orderNumber": "CSH-000501",
  "hash": "a1b2c3d4e5",
  "userId": null,
  "guestId": "g-1",
  "lastName": "Иванов",
  "firstName": "Иван",
  "phone": "+79990000000",
  "email": "user@example.com",
  "deliveryMethod": "cdek_pvz",
  "city": "Москва",
  "pvzCode": "MSK123",
  "pvzAddress": "ул. Примерная, 1",
  "status": "pending",
  "paymentStatus": "CREATED",
  "deliveryStatus": "PENDING",
  "productionStatus": "created",
  "total": 358200,
  "deliveryCost": 30000,
  "promoCode": null,
  "promoDiscount": 0,
  "siteSource": "acme",
  "brandId": 1,
  "paymentMethod": "tbank",
  "createdAt": "2026-09-01T10:00:00.000Z",
  "updatedAt": "2026-09-01T10:00:00.000Z",
  "items": [
    { "id": 900, "orderId": 501, "productId": 123, "sizeId": 45, "quantity": 2, "price": 179100 }
  ]
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Internal server error"}В корзине магазина ничего нет. По коду это неотличимо от настоящего сбоя — баг бэкенда, исходный текст «Корзина пуста» в ответ не попадает, см. notes.—
400{"error":"Выберите пункт выдачи СДЭК"}Доставка в пункт выдачи без указания пункта.—
400{"error":"Укажите улицу и дом для курьерской доставки"}Курьерская доставка без адреса.—
500{"error":"Internal server error"}Не указан город. Та же особенность, что и с пустой корзиной — исходный текст «Укажите город доставки» в ответе не появится.—
500{"error":"Internal server error"}Товар распродан или снят с продажи. Тоже 500, не 400 — см. notes.—
400{"error":"Выбранный способ доставки недоступен"}Магазин отключил этот способ.—
422{"error":"Подтвердите email в профиле, чтобы оформить заказ."}У покупателя не подтверждена почта.—
409{"error":"Промокод больше недоступен: лимит использований исчерпан"}code: PROMO_EXHAUSTEDПромокод есть, но лимит использований выбрали между проверкой и оформлением.—
409{"error":"Basic (M): доступно к продаже только 3 шт."}code: SALES_LIMITНа товар/размер выставлен лимит продаж, и запрошенное количество его превышает. Текст ошибки включает название товара, размер (если есть) и остаток; при полностью исчерпанном лимите — «...: продажи завершены».—
POST/orders/validate-promo

Проверка промокода

Считает скидку до оформления заказа — для показа в корзине.

Доступ:Без авторизации

Тело запроса

ПараметрТипОбязателенОписание
codestringдаПромокод.
cartTotalKopeksnumberдаСумма товаров.
hasDiscountedItemsbooleanнетВ корзине уже есть товары со своей скидкой — часть промокодов с ними не суммируется.

Поля ответа

ПолеТипОписание
validbooleanПрименим ли код.
discountKopeksnumberРазмер скидки.в копейках
messagestringПричина отказа, готовая к показу покупателю. Вариантов больше десятка — не пытайтесь сопоставлять текст логике у себя, просто показывайте как есть.
codestringЭхо применённого кода в верхнем регистре.
promoIdnumberИдентификатор промокода в базе, если он найден.

Ошибки

КодОтвет сервераКогда возникаетЧто делать
200{"valid":false,"message":"Промокод не найден"}Кода нет в этом магазине.Отказ приходит с успешным кодом ответа: причина в поле message, её можно показать покупателю как есть.
400{"error":"Некорректная сумма корзины"}cartTotalKopeks не число или меньше нуля.—
GET/orders/hash/{hash}

Отслеживание заказа

Состояние заказа по публичной ссылке, без входа.

Доступ:Без авторизации

Ссылка для покупателя: по случайному ключу из письма он видит свой заказ и статус доставки. Ключ длинный и неугадываемый, поэтому вход не требуется.

Параметры пути

ПараметрТипОбязателенОписание
hashstringдаКлюч из ссылки.
ответ
{
  "id": 501,
  "orderNumber": "CSH-000501",
  "hash": "a1b2c3d4e5",
  "status": "processing",
  "paymentStatus": "PAID",
  "deliveryStatus": "ACCEPTED",
  "deliveryStatusRu": "Принят на склад",
  "total": 358200,
  "deliveryCost": 30000,
  "city": "Москва",
  "trackingNumber": "1234567890",
  "createdAt": "2026-09-01T10:00:00.000Z",
  "items": [
    {
      "id": 900,
      "quantity": 2,
      "product": { "id": 123, "name": "Футболка Basic", "slug": "futbolka-basic", "images": ["https://cdn.amarix.ru/products/123/1.jpg"], "price": 199000 },
      "size": { "size": "M" }
    }
  ],
  "payment": { "status": "CONFIRMED", "paymentMethod": "tbank", "transactionId": "T-999" }
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
404{"error":"Заказ не найден"}Ключ неверный.—
GET/products/collections

Коллекции

Подборки товаров магазина.

Доступ:Без авторизации

Коллекция — произвольная витринная подборка: капсула сезона, коллаборация, подарочный набор. От категории отличается тем, что товар может лежать сразу в нескольких коллекциях.

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringдаКод магазина.

Параметры запроса

ПараметрТипОбязателенОписание
brandstringнетКод магазина как альтернатива заголовку X-Site-Source.
publictrueнетТот же смысл, что у каталога — отсечь скрытые коллекции.

Поля ответа

ПолеТипОписание
idstringЗначение поля Product.collectionId — строка (не число), им фильтруют каталог через ?collectionId=.
namestringТо же значение collectionId, но обрезанное от пробелов (trim). Отдельного поля названия коллекции в базе нет.
GET/categories/{id}

Одна категория

Категория с её родителем и вложенными.

Доступ:Без авторизации

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетФормально принимается, но на этот путь не действует — сервер применяет скоуп по бренду только когда запрос пришёл от сотрудника, а не с витрины. Категория чужого бренда всё равно отдастся, если знать её id.

Параметры пути

ПараметрТипОбязателенОписание
idintдаЧисловой идентификатор. Адресное имя не принимается, несмотря на название пути, — нечисловое значение даёт NaN и падает в 404.
ответ
{
  "id": 5,
  "name": "Футболки",
  "slug": "futbolki",
  "moySkladId": null,
  "displayOrder": 0,
  "description": "Мужские и женские футболки.",
  "isVisible": true,
  "createdAt": "2026-01-01T00:00:00.000Z",
  "updatedAt": "2026-01-01T00:00:00.000Z",
  "deletedAt": null,
  "brands": [{ "id": 1, "code": "acme", "name": "Acme" }],
  "brandIds": [1]
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
404{"error":"Категория не найдена"}Категории нет в этом магазине.—
GET/products/favorites

Избранное

Товары, отмеченные покупателем.

Доступ:Сессия сотрудника

Только для вошедшего покупателя: у гостя избранного нет. Если оно нужно до входа, держите список у себя и перенесите после входа.

ответ
[
  {
    "id": "123",
    "slug": "futbolka-basic",
    "moySkladId": "ms-123",
    "name": "Футболка Basic",
    "description": "Хлопок 100%",
    "price": 199000,
    "discount": 10,
    "images": ["https://cdn.amarix.ru/products/123/1.jpg"],
    "category": { "id": 5, "name": "Футболки", "slug": "futbolki" },
    "sizes": [
      {
        "id": 45,
        "size": "M",
        "quantity": 12,
        "variantId": "v-1",
        "isVisible": true
      }
    ],
    "inStock": true,
    "productionTime": 3,
    "assemblyTime": 1,
    "isDeleted": false,
    "discountType": "percent",
    "displayOrder": 0,
    "brand": "acme",
    "material": "Хлопок",
    "care": "Стирка 30°",
    "collectionId": "12",
    "sizeGrid": [],
    "badgeText": "Новинка",
    "badgeColor": "#FF0000",
    "imageMetadata": {}
  }
]

Ошибки

КодОтвет сервераКогда возникаетЧто делать
401{"error":"Unauthorized"}Токена нет вовсе (и нет валидного refresh-токена в cookie). Точный текст зависит от причины — сервер отдаёт его в middleware авторизации, а не в контроллере избранного: "Session expired" при просроченной/неактивной сессии, "User not found" если пользователь из токена удалён. Проверка "User not authenticated" в контроллере — мёртвый код, до него не доходит.—
POST/products/favorites/{productId}/toggle

Отметить или снять

Переключает товар в избранном.

Доступ:Сессия сотрудника

Одна кнопка на оба действия: был отмечен — снимется, не был — отметится.

Параметры пути

ПараметрТипОбязателенОписание
productIdintдаТовар.

Поля ответа

ПолеТипОписание
isFavoritebooleanСостояние после переключения.

Ошибки

КодОтвет сервераКогда возникаетЧто делать
401{"error":"Unauthorized"}Токена нет вовсе (и нет валидного refresh-токена в cookie). Как и у списка избранного, точный текст зависит от причины — его отдаёт middleware авторизации: "Session expired" при просроченной/неактивной сессии, "User not found" если пользователь из токена удалён. "User not authenticated" в контроллере — мёртвый код.—
GET/products/favorites/{productId}/check

Проверить избранное

Отмечен ли товар этим покупателем.

Доступ:Сессия сотрудника

Параметры пути

ПараметрТипОбязателенОписание
productIdintдаТовар.

Поля ответа

ПолеТипОписание
isFavoritebooleanОтмечен ли.

Ошибки

КодОтвет сервераКогда возникаетЧто делать
401{"error":"Unauthorized"}Токена нет вовсе (и нет валидного refresh-токена в cookie). Как и у остальных эндпоинтов избранного, точный текст зависит от причины — его отдаёт middleware авторизации: "Session expired" при просроченной/неактивной сессии, "User not found" если пользователь из токена удалён. "User not authenticated" в контроллере — мёртвый код.—

Учётные записи покупателей

Регистрация, вход и восстановление пароля живут по адресам, начинающимся с /auth. Учётная запись покупателя привязана к магазину: одна и та же почта в двух магазинах — это два разных покупателя, и войти учётной записью соседнего магазина нельзя.

Чего на витрине нет

Чтобы не искать: в платформе отсутствуют как сущности

  • отзывы и оценки товаров — их негде хранить и нечем отдавать;
  • подписка на поступление и подписка на снижение цены как отдельная функция.

Уведомление о поступлении всё же работает, но иначе: письмо уходит тем, у кого товар в избранном и подтверждена почта, когда сотрудник снимает пометку «продано». То есть роль подписки играет избранное.