Справочник API

Корзина и оформление

Корзина, расчёт доставки СДЭК, пункты выдачи и оплата. Вход покупателю не нужен: гостевая корзина держится на паре кук, которые платформа выдаёт сама. Если покупатель потом войдёт, его гостевая корзина подхватится.

Порядок действий

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

  1. Кладём товары — POST /cart/items.
  2. Ищем город — GET /orders/cities, запоминаем его код.
  3. Для пункта выдачи берём список — GET /orders/pvzs/{cityCode}.
  4. Считаем доставку — POST /orders/calculate-delivery — и показываем сумму.
  5. Создаём заказ — POST /orders.
  6. Ведём на оплату — POST /payment/create.

Товар не пропадает со склада при оформлении

Заказ не списывает остаток, а закрепляет товар за собой. Списание происходит в момент отгрузки. Поэтому брошенная корзина или неоплаченный заказ не «съедают» витрину навсегда: закрепление снимается само.

Отсюда следствие для интерфейса: остаток, который отдаёт корзина, — доступный, то есть уже за вычетом чужих закреплений. Складывать его с чем-то ещё не нужно.

Деньги в копейках

Все суммы в API целые и в копейках: 450000 — это 4500 ₽. Дробных рублей не бывает нигде, включая доставку и скидки.

проверить, что API отвечает
curl -s "https://api.amarix.ru/orders/cities?search=Москва" \
  -H "X-Site-Source: acme"

Корзина, расчёт доставки СДЭК, пункты выдачи и оплата. Вход покупателю не нужен: гостевая корзина держится на паре кук, которые платформа выдаёт сама. Если покупатель потом войдёт, его гостевая корзина подхватится.

POST/cart/session

Пропуск гостя

Заводит корзину для покупателя без входа.

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

Отдельно вызывать не обязательно: если обратиться к любому /cart-эндпоинту вообще без пропуска, платформа заведёт его сама. Явный вызов нужен, когда хочется получить пропуск заранее — например, чтобы показать пустую корзину до первого действия.

Поля ответа

ПолеТипОписание
guestIdstringИдентификатор гостевой корзины.
guestTokenstringПодпись, подтверждающая владение корзиной.
GET/cart

Корзина

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

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

Для вошедшего покупателя — его корзина, для гостя — та, что привязана к пропуску. Остаток считается доступный: физический минус то, что уже закреплено за чужими неотгруженными заказами.

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетКод магазина. Сервер его не требует — без заголовка подставляется бренд по умолчанию (acme). Для настоящей интеграции передавайте всегда.
X-Guest-IDstringнетПропуск гостя, если покупатель не вошёл.
X-Guest-TokenstringнетПодпись пропуска. Обязателен вместе с X-Guest-ID — без него 401.
ответ
{
  "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,
        "sizes": [
          { "id": 45, "size": "M", "quantity": 12, "availableQuantity": 12, "isVisible": true, "price": 199000 }
        ]
      },
      "size": "M",
      "quantity": 2,
      "total": 358200
    }
  ],
  "total": 358200
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
401{"error":"Invalid guest session"}X-Guest-Token не сходится с X-Guest-ID.—
POST/cart/items

Положить в корзину

Добавляет размер товара; если он уже там — увеличивает количество.

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

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетКод магазина. Не enforced сервером — без него подставляется бренд по умолчанию.
X-Guest-IDstringнетПропуск гостя.
X-Guest-TokenstringнетПодпись пропуска. Обязателен вместе с X-Guest-ID.

Тело запроса

ПараметрТипОбязателенОписание
productIdintдаТовар.
sizeIdintнетЛегаси-адресация размера. Нужен либо sizeId, либо variantId.
variantIdintнетВариант товара (система атрибутов) — альтернатива sizeId, обязателен для товара без легаси-размера. Нужен либо sizeId, либо variantId.
quantityintдаСколько добавить.
ответ
{
  "id": 987,
  "items": [
    {
      "id": 55,
      "product": {
        "id": "123",
        "name": "Футболка Basic",
        "slug": "futbolka-basic",
        "price": 199000,
        "images": ["https://cdn.amarix.ru/products/123/1.jpg"],
        "inStock": true,
        "sizes": [
          { "id": 45, "size": "M", "quantity": 12, "availableQuantity": 12, "isVisible": true, "price": 199000 }
        ]
      },
      "size": "M",
      "quantity": 1,
      "total": 199000
    }
  ],
  "total": 199000
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Internal server error"}Любая ошибка сервиса — неверный товар/размер, превышен лимит продаж и т.д. Текст не различает причину, см. notes.—
PUT/cart/items/{id}

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

Ставит новое количество для позиции.

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

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringнетКод магазина. Не enforced сервером — без него подставляется бренд по умолчанию.
X-Guest-IDstringнетПропуск гостя.
X-Guest-TokenstringнетПодпись пропуска. Обязателен вместе с X-Guest-ID.

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

ПараметрТипОбязателенОписание
idintдаИдентификатор позиции корзины.

Тело запроса

ПараметрТипОбязателенОписание
quantityintдаНовое количество.
ответ
{
  "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",
      "quantity": 3,
      "total": 537300
    }
  ],
  "total": 537300
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Internal server error"}Позиции или корзины нет, либо любая другая ошибка сервиса. Реальные причины («Корзина не найдена», «Товар не найден в корзине») не документированы отдельными кодами — контроллер отдаёт для них всех одинаковый 500.—
DELETE/cart/items/{id}

Убрать позицию

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

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

Заголовки

ПараметрТипОбязателенОписание
X-Guest-IDstringнетПропуск гостя.
X-Guest-TokenstringнетПодпись пропуска. Обязателен вместе с X-Guest-ID.

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

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

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Internal server error"}Позиции или корзины нет — отдельного кода для этого случая нет, см. cart-update.—
DELETE/cart

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

Убирает все позиции разом.

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

Заголовки

ПараметрТипОбязателенОписание
X-Guest-IDstringнетПропуск гостя.
GET/orders/cities

Города доставки

Поиск города по названию.

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

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

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

ПараметрТипОбязателенОписание
qstringнетЧасть названия или региона. Имя параметра именно q, не search — search молча игнорируется, поиск станет пустым.

Поля ответа

ПолеТипОписание
codeintКод города у перевозчика.
citystringНазвание.
regionstringРегион — им различают одноимённые города.

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Internal server error"}Параметр q не передан вовсе — сервис падает на пустом запросе.—
GET/orders/pvzs/{cityCode}

Пункты выдачи

Пункты выдачи в городе с координатами.

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

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

ПараметрТипОбязателенОписание
cityCodeintдаКод города из /orders/cities.

Поля ответа

ПолеТипОписание
locationobjectКоординаты и код города — вложенный объект, а не плоские поля.
location.city_codeintКод города — совпадает с запрошенным cityCode.
location.latitudefloatШирота — для карты. Именно location.latitude, не плоское latitude.
location.longitudefloatДолгота, там же.
GET/orders/pvzs-near

Пункты выдачи рядом с точкой

Пункты выдачи в радиусе от координат — альтернатива поиску по городу.

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

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

ПараметрТипОбязателенОписание
latnumberдаШирота точки.
lngnumberдаДолгота точки.
radiusnumberнетпо умолчанию 40Радиус поиска, км.
ответ
[
  {
    "location": { "city_code": 44, "latitude": 55.751244, "longitude": 37.618423 }
  }
]

Ошибки

КодОтвет сервераКогда возникаетЧто делать
400{"error":"lat/lng обязательны"}lat или lng не число.—
POST/orders/calculate-delivery

Расчёт доставки

Стоимость и срок доставки для собранной корзины.

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

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

Заголовки

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

Тело запроса

ПараметрТипОбязателенОписание
addressobjectдаНе плоские поля, а вложенный объект — единственный настоящий контракт тела.
address.citystringдаГород доставки. Сервер не проверяет обязательность — без него упадёт 500.
address.postalCodestringнетИндекс.
address.isCourierbooleanнетtrue — курьер, иначе (по умолчанию) — пункт выдачи.
items[].quantityintнетКоличество единиц позиции. По умолчанию 1.
items[].product.weightnumberнетВес единицы, кг. По умолчанию 0.5.
items[].product.lengthnumberнетДлина упаковки, см. По умолчанию 30.
items[].product.widthnumberнетШирина упаковки, см. По умолчанию 20.
items[].product.heightnumberнетВысота упаковки, см. По умолчанию 10.

Поля ответа

ПолеТипОписание
costKopecksintИтоговая стоимость доставки — уже с учётом бесплатной доставки и наценки.копейки
costnumberТо же самое в рублях.
daysintСрок доставки, дней. Одно число, не диапазон — periodMin/periodMax не существуют.
freeShippingbooleanДоставка бесплатна. Поле называется freeShipping, не free.
freeShippingReasonstring | nullПочему бесплатна (или почему нет), если применимо.
freeShippingThresholdKopecksint | nullПорог бесплатной доставки магазина.копейки
tariffs[].codeintКод тарифа СДЭК.
tariffs[].namestringНазвание тарифа.
tariffs[].costnumberСтоимость в рублях.
tariffs[].costKopecksintТа же стоимость в копейках.копейки
tariffs[].daysintСрок по этому тарифу, дней.
POST/orders

Создать заказ

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

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

Товар при этом не списывается со склада, а закрепляется за заказом: списание происходит в момент отгрузки. Поэтому создание заказа не «съедает» остаток витрины навсегда — неоплаченный резерв освободится сам.

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringдаКод магазина.
X-Guest-IDstringнетПропуск гостя.
X-CSRF-TokenstringнетКлюч из /auth/csrf. Заявлен обязательным, но фактически на этом роуте не проверяется — CSRF-middleware сюда не навешан. Присылайте его всё равно: это может измениться без предупреждения.

Тело запроса

ПараметрТипОбязателенОписание
lastNamestringдаФамилия получателя.
firstNamestringдаИмя.
middleNamestringнетОтчество.
phonestringдаТелефон.
emailstringнетПочта — на неё уйдёт письмо с заказом. Проверяется поверх (подтверждённость), но сама обязательность поля сервером не проверена.
telegramstringнетКонтакт в Telegram.
citystringнетГород. Обязательность не проверяется сервером — пустое значение приведёт к 500, а не к понятной ошибке.
cityCodeintнетКод города.
deliveryMethodcdek_pvz | cdek_courier | internationalнетСпособ доставки. Обязательность не проверяется сервером.
pvzCodestringнетКод пункта выдачи для cdek_pvz.
pvzAddressstringнетАдрес пункта выдачи текстом.
streetstringнетУлица для курьера.
housestringнетДом.
apartmentstringнетКвартира.
entrancestringнетПодъезд.
floorstringнетЭтаж.
intercomstringнетДомофон.
postalCodestringнетИндекс.
innstringнетИНН — для покупки от юрлица/ИП.
promoCodestringнетПромокод, уже проверенный через /orders/validate-promo.
commentstringнетКомментарий покупателя.
paymentMethodstringнетСпособ оплаты.
agreedToTermsbooleanнетСогласие с условиями. Сервер проверяет только истинность значения, не то, что поле прислано — по факту необязательное.
agreedToPrivacybooleanнетСогласие на обработку данных. Та же оговорка.
agreedToMarketingbooleanнетСогласие на рассылку.
marketingUtmCampaignstringнетUTM-метка рекламной кампании.

Поля ответа

ПолеТипОписание
idintИдентификатор заказа.
orderNumberstringНомер для покупателя.
hashstringКлюч для страницы отслеживания без входа.
totalintИтого с доставкой.копейки
deliveryCostintДоставка.копейки

Ошибки

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

Ссылка на оплату

Заводит платёж и возвращает адрес платёжной страницы.

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

После создания заказа отправьте покупателя по этому адресу. Итог платежа платформа узнаёт сама от банка — опрашивать её не нужно, статус заказа обновится.

Заголовки

ПараметрТипОбязателенОписание
X-Site-SourcestringдаКод магазина.
X-CSRF-TokenstringнетКлюч из /auth/csrf. Заявлен обязательным, но фактически на этом роуте не проверяется. Присылайте всё равно.

Тело запроса

ПараметрТипОбязателенОписание
orderIdintдаЗаказ, который оплачиваем.
paymentMethodstringнетСпособ оплаты. Фактически на сервере игнорируется: параметр нигде не используется дальше, платёж всегда заводится через Т-Банк независимо от значения. Можно не присылать вовсе.

Поля ответа

ПолеТипОписание
paymentIdstringВнутренний идентификатор строки платежа в БД. НЕ идентификатор транзакции у банка — тот в этот ответ не попадает вообще.
orderIdintОплачиваемый заказ.
orderNumberstringНомер заказа для покупателя.
orderHashstringКлюч страницы отслеживания.
amountnumberСумма к оплате.
paymentMethodstringВсегда возвращает "tbank", независимо от того, что было прислано в теле запроса (или не прислано вовсе) — переданное значение сервер не учитывает.
paymentUrlstringАдрес платёжной страницы.
statusstringСтатус свежесозданного платежа.

Ошибки

КодОтвет сервераКогда возникаетЧто делать
500{"error":"Internal server error"}Заказ уже оплачен ("Оплата уже выполнена") либо заказ не найден ("Заказ не найден") — обе проверки бросают ошибку без кода статуса и проваливаются в общий catch. Документированные тексты «Заказ уже оплачен» (400) и «Заказ не найден» (404) в ответе НЕ появляются — реальный текст всегда «Internal server error», а код всегда 500. Это баг бэкенда.—
400{"error":"Онлайн-оплата для этого магазина пока не настроена..."}code: TBANK_NOT_CONFIGUREDУ бренда нет настроенной интеграции с Т-Банком.—
GET/orders

Мои заказы

История заказов вошедшего покупателя.

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

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

ПараметрТипОбязателенОписание
limitintнетЗаявлено, но сервер эти query-параметры не читает вообще — пагинации нет, отдаётся вся история сразу.
offsetintнетТа же оговорка, что у limit — игнорируется.

Поля ответа

ПолеТипОписание
idintИдентификатор.
orderNumberstringНомер.
statusstringСтатус заказа.
paymentStatusstringСтатус оплаты.
deliveryStatusstringСтатус доставки.
deliveryStatusRustringТот же статус по-русски.
trackingNumberstringТрек-номер, когда накладная создана.
totalintСумма.копейки
itemsobject[]Состав заказа.
paymentobjectСтатус и способ последнего платежа: { status, paymentMethod }.

Ошибки

КодОтвет сервераКогда возникаетЧто делать
401{"error":"Unauthorized"}Покупатель не вошёл.—
GET/orders/{id}

Один заказ

Подробности заказа покупателя.

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

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

ПараметрТипОбязателенОписание
idintдаИдентификатор заказа.
ответ
{
  "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" }
}

Ошибки

КодОтвет сервераКогда возникаетЧто делать
400{"error":"Неверный ID заказа"}id в пути не число.—
404{"error":"Заказ не найден"}Заказа нет или он чужой.—

Дальше: вход и личный кабинет — история заказов, адреса и бонусы уже требуют входа.