Skip to content
MiniShop3
MiniShop3
Современный компонент интернет-магазина для MODX 3
  1. Компоненты
  2. MiniShop3
  3. Разработка
  4. REST API

REST API

Web API MiniShop3 (api.php) обслуживает витрину и headless-клиенты: корзина, checkout, ЛК покупателя, публичный каталог. Manager API (connector.php) — отдельно, под сессией MODX для Vue-админки.

Источник роутов витрины: core/components/minishop3/config/routes/web.php. Свои роуты: core/config/ms3_routes_web.custom.php, фрагменты аддонов: core/config/ms3.routes.d/web/*.php.

Точки входа

НазначениеURLАвторизация
Web API (витрина / headless)/assets/components/minishop3/api.phpТокен: cookie ms3_token, Authorization: Bearer, legacy MS3TOKEN
Manager API (админка)/assets/components/minishop3/connector.phpСессия MODX

Эта страница описывает Web API. Manager REST: Backend API, Маршрутизация.

Базовый URL

/assets/components/minishop3/api.php?route=/api/v1/{endpoint}

Все запросы передают маршрут через параметр route. Для cookie-токена указывайте credentials: 'include'. CORS и rate limit настраиваются ключами ms3_cors_* и ms3_rate_limit_* (Системные настройки).

Карта эндпоинтов

Источник: config/routes/web.php. Группа /api/v1 всегда проходит CORS, rate limit и ServiceCheck.

МетодПутьТокен
POST/cart/addгостевой
POST/cart/removeгостевой
POST/cart/changeгостевой
POST/cart/change-optionгостевой
GET/cart/getгостевой
POST/cart/cleanгостевой
GET/order/getгостевой
POST/order/addгостевой
POST/order/setгостевой
POST/order/removeгостевой
POST/order/submitгостевой
POST/order/cleanгостевой
GET/order/costгостевой
GET/order/cost/cartгостевой
GET/order/cost/deliveryгостевой
GET/order/cost/paymentгостевой
POST/order/address/setгостевой
POST/order/address/cleanгостевой
GET/order/delivery/validation-rulesгостевой
GET/order/delivery/required-fieldsгостевой
POST/customer/loginнет
POST/customer/registerнет
POST/customer/logoutавторизованный
POST/customer/forgot-passwordнет
POST/customer/reset-passwordнет
POST/customer/addавторизованный
GET/customer/token/getнет
GET/customer/addressesавторизованный
GET/customer/addresses/{id}авторизованный
POST/customer/addressesавторизованный
PUT/customer/addresses/{id}авторизованный
DELETE/customer/addresses/{id}авторизованный
PUT/customer/addresses/{id}/set-defaultавторизованный
PUT/customer/profileавторизованный
POST/customer/changeAddressгостевой
POST/customer/email/resend-verificationавторизованный
GET/customer/email/verifyнет
GET/customer/ordersавторизованный
GET/customer/orders/{id}авторизованный
POST/customer/orders/{id}/cancelавторизованный
GET/product/get/{id}нет
GET/product/listнет
GET/healthнет

«Гостевой» токен: GET /customer/token/get (корзина и черновик заказа). «Авторизованный»: после login / register.

Программное создание заказа без сессии (extras/cron) — не Web HTTP. См. ProgrammaticOrderService.

Авторизация

Получение токена

Перед работой с корзиной и заказами необходимо получить токен клиента:

http
GET /api/v1/customer/token/get

Ответ:

json
{
  "success": true,
  "data": {
    "token": "abc123def456..."
  },
  "message": ""
}

Начиная с версии 1.6, токен хранится в httpOnly cookie ms3_token. Сервер автоматически устанавливает cookie при получении/обновлении токена.

Безопасность

httpOnly cookie недоступна из JavaScript, что защищает токен от XSS-атак. Браузер автоматически передаёт cookie при каждом запросе.

Порядок разрешения токена на сервере (TokenMiddleware):

  1. Заголовок Authorization: Bearer {token} (для мобильных приложений)
  2. Заголовок HTTP_MS3TOKEN (legacy)
  3. httpOnly cookie ms3_token (основной способ для веба)

Cookie настраивается с учётом параметров MODX-сессии: session_cookie_domain, session_cookie_path, session_cookie_secure, session_cookie_samesite.

Формат ответов

Все ответы имеют единый формат:

Успех:

json
{
  "success": true,
  "data": { ... },
  "message": "Сообщение об успехе"
}

Ошибка:

json
{
  "success": false,
  "message": "Описание ошибки",
  "code": 400
}

Корзина

Добавить товар

http
POST /api/v1/cart/add

Параметры:

ПараметрТипОбязательныйОписание
idintДаID товара
countintНетКоличество (по умолчанию 1)
optionsobjectНетОпции товара (цвет, размер и т.д.)
renderarrayНетТокены сниппетов для SSR

Пример запроса:

javascript
fetch('/assets/components/minishop3/api.php?route=/api/v1/cart/add&ms3_token=' + token, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        id: 123,
        count: 2,
        options: {
            color: 'Красный',
            size: 'XL'
        }
    })
})

Ответ:

json
{
  "success": true,
  "data": {
    "last_key": "123_a1b2c3d4",
    "cart": [
      {
        "key": "123_a1b2c3d4",
        "id": 123,
        "count": 2,
        "price": 1500,
        "cost": 3000,
        "weight": 0.5,
        "options": {"color": "Красный", "size": "XL"},
        "name": "Товар",
        "thumb": "/assets/images/product.jpg"
      }
    ],
    "status": {
      "total_count": 2,
      "total_cost": 3000,
      "total_weight": 1.0,
      "total_positions": 1
    }
  },
  "message": "Товар добавлен в корзину"
}

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

http
POST /api/v1/cart/change

Параметры:

ПараметрТипОбязательныйОписание
product_keystringДаУникальный ключ товара в корзине
countintДаНовое количество

Пример:

json
{
  "product_key": "123_a1b2c3d4",
  "count": 5
}

Удалить товар

http
POST /api/v1/cart/remove

Параметры:

ПараметрТипОбязательныйОписание
product_keystringДаУникальный ключ товара

Изменить опции товара

http
POST /api/v1/cart/change-option

Меняет опции позиции в корзине (ключ позиции может измениться).

Параметры:

ПараметрТипОбязательныйОписание
product_keystringДаКлюч позиции в корзине
optionsobjectДаНовые опции (непустой объект)

Пример:

json
{
  "product_key": "123_a1b2c3d4",
  "options": {
    "color": "red",
    "size": "L"
  }
}

Получить корзину

http
GET /api/v1/cart/get

Ответ:

json
{
  "success": true,
  "data": {
    "cart": [...],
    "status": {
      "total_count": 5,
      "total_cost": 7500,
      "total_weight": 2.5,
      "total_positions": 3
    }
  }
}

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

http
POST /api/v1/cart/clean

Заказ

Получить черновик заказа

http
GET /api/v1/order/get

Ответ:

json
{
  "success": true,
  "data": {
    "order": {
      "id": 0,
      "delivery_id": 1,
      "payment_id": 1,
      "order_comment": "",
      "cart_cost": 1500,
      "delivery_cost": 300,
      "cost": 1800,
      "address_email": "user@example.com",
      "address_phone": "+79991234567",
      "address_first_name": "Иван",
      "address_last_name": "Иванов",
      "address_city": "Москва",
      "address_street": "Ленина",
      "address_comment": ""
    }
  }
}

В data только объект order (поля msOrder + адрес с префиксом address_). Списки способов доставки и оплаты этот эндпоинт не отдаёт.

Добавить/обновить поле

http
POST /api/v1/order/add

Параметры:

ПараметрТипОбязательныйОписание
keystringДаИмя поля
valuemixedДаЗначение

Доступные поля:

ПолеОписание
emailEmail (пишется в адрес)
phoneТелефон (адрес)
first_nameИмя (адрес)
last_nameФамилия (адрес)
delivery_idID способа доставки
payment_idID способа оплаты
order_commentКомментарий к заказу (msOrder)
commentКомментарий к адресу (msOrderAddress)
cityГород
streetУлица
buildingДом
roomКвартира/офис
indexИндекс
address_hashХеш сохранённого адреса

В add / set ключи адреса — без префикса (city, first_name). В ответе order/get те же поля приходят как address_city, address_first_name.

Пример:

json
{
  "key": "email",
  "value": "user@example.com"
}

Установить несколько полей

http
POST /api/v1/order/set

Параметры:

ПараметрТипОбязательныйОписание
fieldsobjectДаОбъект с полями

Пример:

json
{
  "fields": {
    "email": "user@example.com",
    "phone": "+79991234567",
    "first_name": "Иван",
    "delivery_id": 1,
    "payment_id": 2,
    "order_comment": "Позвоните перед доставкой"
  }
}

При ошибке хотя бы одного поля ответ success: false, сообщение ms3_order_err_validation, в data:

json
{
  "order": { },
  "errors": {
    "email": "…",
    "delivery_id": "…"
  }
}

Каждое поле по-прежнему проходит через add() и события msOnBeforeAddToOrder / msOnAddToOrder. set() только агрегирует ошибки.

Удалить поле

http
POST /api/v1/order/remove

Параметры:

ПараметрТипОбязательныйОписание
keystringДаИмя поля

Оформить заказ

http
POST /api/v1/order/submit

Ответ (успех):

json
{
  "success": true,
  "data": {
    "order_id": 15,
    "order_num": "24/12-15",
    "redirect_url": "/thank-you?msorder=15"
  },
  "message": "Заказ успешно оформлен"
}

Ответ (ошибка валидации):

json
{
  "success": false,
  "message": "Заполните обязательные поля",
  "data": {
    "errors": {
      "email": "Укажите email",
      "phone": "Укажите телефон"
    }
  },
  "code": 400
}

Очистить заказ

http
POST /api/v1/order/clean

Стоимость

Полная стоимость

http
GET /api/v1/order/cost

Ответ:

json
{
  "success": true,
  "data": {
    "cart_cost": 5000,
    "delivery_cost": 300,
    "payment_cost": 0,
    "total_cost": 5300,
    "discount": 0
  }
}

Стоимость корзины

http
GET /api/v1/order/cost/cart

Стоимость доставки

http
GET /api/v1/order/cost/delivery

Комиссия оплаты

http
GET /api/v1/order/cost/payment

Адреса доставки

Установить сохранённый адрес

http
POST /api/v1/order/address/set

Параметры:

ПараметрТипОбязательныйОписание
address_hashstringДаMD5 хеш адреса

Очистить адрес

http
POST /api/v1/order/address/clean

Валидация доставки

Правила валидации

http
GET /api/v1/order/delivery/validation-rules

Ответ:

json
{
  "success": true,
  "data": {
    "city": {"required": true, "min": 2},
    "street": {"required": true},
    "building": {"required": true},
    "phone": {"required": true, "pattern": "^\\+?[0-9]+$"}
  }
}

Обязательные поля

http
GET /api/v1/order/delivery/required-fields

Ответ:

json
{
  "success": true,
  "data": ["city", "street", "building", "phone"]
}

Клиент

Регистрация

http
POST /api/v1/customer/register

Параметры:

ПараметрТипОбязательныйОписание
emailstringДаEmail
passwordstringДаПароль
first_namestringНетИмя
last_namestringНетФамилия
phonestringНетТелефон
privacy_acceptedboolЗависит от настроекСогласие на обработку данных

Контроллер передаёт в процессор только эти поля. password_confirm в Web API регистрации не используется (нужен для reset-password).

Ответ:

json
{
  "success": true,
  "object": {
    "customer": {
      "id": 5,
      "email": "user@example.com",
      "first_name": "Иван",
      "last_name": "Петров",
      "phone": "+79991234567",
      "email_verified": false
    },
    "token": "abc123def456...",
    "expires_at": "2026-03-16 12:34:56",
    "email_verification_required": false,
    "redirect_url": ""
  },
  "message": "Регистрация успешна"
}

Breaking change (v1.6)

Формат ответа регистрации изменён:

  • Было (v1.5): token — объект {token: "...", expires_at: "..."}
  • Стало (v1.6): token — строка, expires_at вынесен на верхний уровень

Кастомные темы, обращающиеся к result.object.token.token, нужно обновить на result.object.token.

Авторизация

http
POST /api/v1/customer/login

Параметры:

ПараметрТипОбязательныйОписание
emailstringДаEmail
passwordstringДаПароль

Ответ:

json
{
  "success": true,
  "data": {
    "customer_id": 5,
    "token": "session_token_xyz789",
    "expires_at": "2026-08-19 12:00:00",
    "customer": {
      "id": 5,
      "email": "user@example.com",
      "first_name": "Иван"
    }
  }
}

Ротация токена

После login / register / успешного email/verify сервер всегда выдаёт новый API-токен (AuthManager::establishCustomerSession). Старый гостевой или предыдущий cookie ms3_token отзывается. Черновик корзины переносится на новый токен (transferDraftToToken / bindDraftToCustomer), затем session_regenerate_id(true).

Headless-клиент обязан сохранить новый token / expires_at из ответа. На витрине с httpOnly cookie браузер получает обновление cookie сам.

javascript
const res = await fetch('/api/v1/customer/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'ms3-token': guestToken, // текущий гостевой токен
  },
  body: JSON.stringify({ email, password }),
})
const json = await res.json()
if (!json.success) throw new Error(json.message)

// Обязательно подменить токен во всех следующих запросах
const { token, expires_at, customer_id } = json.data
localStorage.setItem('ms3_token', token)
localStorage.setItem('ms3_token_expires', expires_at)

Выход

http
POST /api/v1/customer/logout

Требует авторизованного токена. Завершает сессию покупателя.

Восстановление пароля

http
POST /api/v1/customer/forgot-password

Токен не нужен. Rate limit по email (1 запрос / 5 минут).

ПараметрТипОбязательныйОписание
emailstringДаEmail покупателя

Ответ всегда успешен с точки зрения UX (не раскрывает, есть ли аккаунт), письмо уходит только если клиент найден.

Сброс пароля

http
POST /api/v1/customer/reset-password

Токен не нужен (токен сброса приходит в письме).

ПараметрТипОбязательныйОписание
tokenstringДаТокен из письма
passwordstringДаНовый пароль
password_confirmstringДаПодтверждение пароля

Обновить профиль

http
PUT /api/v1/customer/profile

Требует авторизации (токен авторизованного клиента).

Параметры:

ПараметрТипОписание
first_namestringИмя
last_namestringФамилия
phonestringТелефон

Быстрое обновление поля профиля

http
POST /api/v1/customer/add

Требует авторизации. Обновляет одно поле msCustomer (key + value). Разрешены editable-ключи из xPDO-карты (с denylist служебных полей). Для email при смене сбрасывается email_verified_at.

ПараметрТипОбязательныйОписание
keystringДаИмя поля
valuemixedДаНовое значение

Выбрать сохранённый адрес в черновике

http
POST /api/v1/customer/changeAddress

Нужен гостевой токен. Аналог POST /order/address/set: в черновик подставляется адрес по address_hash (допускается legacy-поле value).

ПараметрТипОбязательныйОписание
address_hashstringДаHash адреса покупателя

Верификация email

http
GET /api/v1/customer/email/verify?token=verification_token

Повторная отправка верификации

http
POST /api/v1/customer/email/resend-verification

Требует авторизации.

Адреса клиента

Все endpoints требуют авторизации.

Список адресов

http
GET /api/v1/customer/addresses

Ответ:

json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "hash": "abc123...",
      "city": "Москва",
      "street": "Ленина",
      "building": "10",
      "room": "5",
      "is_default": true
    }
  ]
}

Получить адрес

http
GET /api/v1/customer/addresses/{id}

Создать адрес

http
POST /api/v1/customer/addresses

Параметры:

ПараметрТипОписание
citystringГород
streetstringУлица
buildingstringДом
roomstringКвартира/офис
indexstringИндекс
countrystringСтрана
regionstringРегион
is_defaultboolАдрес по умолчанию

Обновить адрес

http
PUT /api/v1/customer/addresses/{id}

Удалить адрес

http
DELETE /api/v1/customer/addresses/{id}

Установить адрес по умолчанию

http
PUT /api/v1/customer/addresses/{id}/set-default

Заказы клиента

Нужен токен авторизованного покупателя. Черновики в списке и карточке не отдаются.

Список заказов

http
GET /api/v1/customer/orders?limit=20&offset=0&status=2
ПараметрТипОписание
limitintРазмер страницы (по умолчанию 20, максимум 100)
offsetintСмещение
statusintФильтр по status_id. Черновик и невалидные ID игнорируются

Ответ data:

json
{
  "orders": [
    {
      "id": 15,
      "uuid": "...",
      "num": "2603/1",
      "cost": 3500,
      "status_id": 2,
      "status_name": "Новый",
      "can_cancel": true
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}

Карточка заказа

http
GET /api/v1/customer/orders/{id}

Возвращает заказ покупателя с позициями и связанными сущностями. 404, если заказ чужой, не найден или черновик.

Отмена заказа

http
POST /api/v1/customer/orders/{id}/cancel

Отменяет заказ, если текущий статус входит в ms3_customer_cancel_allowed_statuses.

Ответ (успех):

json
{
  "success": true,
  "message": "Заказ отменён",
  "data": {
    "order_id": 15,
    "status_id": 5
  }
}

Ошибки: 400 (статус не разрешён), 404 (не найден), 401 (нет авторизации).

Связанные настройки:

НастройкаОписание
ms3_customer_cancel_allowed_statusesID статусов, при которых разрешена отмена (по умолчанию 2,3)
ms3_status_canceledID целевого статуса отмены

Каталог товаров

Публичные эндпоинты. Токен не нужен. Отдаются только опубликованные, неудалённые товары без hidemenu в запрошенном (или текущем) контексте.

ProductCatalogService формирует ответ и обрезает его allowlist-ом полей ресурса и msProductData. Плагин на msOnGetProductFields может менять значения существующих ключей, но не добавить произвольные поля в JSON каталога. Для headless-витрины без msProducts см. также Каталог.

Один товар

http
GET /api/v1/product/get/{id}
Параметр путиОписание
idID ресурса товара

400 без id, 404 если товар не найден или не публичный.

Список товаров

http
GET /api/v1/product/list?parent=5&limit=20&page=1&sort=price&dir=ASC
ПараметрТипОписание
parent / categoryintID родительской категории (основной parent ресурса)
limitintПо умолчанию 20, максимум 100
offsetintСмещение. Альтернатива: page (с 1)
pageintНомер страницы, если offset не задан
sortstringid, pagetitle, menuindex, createdon, publishedon, price, article
dir / sortdirstringASC или DESC
querystringПоиск по pagetitle / article
contextstringКлюч контекста MODX
include_options0 / 1Включить опции (по умолчанию 0)
include_content0 / 1Включить content (по умолчанию 0)

Ответ data:

json
{
  "items": [ { "id": 10, "pagetitle": "Товар", "price": 1500 } ],
  "total": 42,
  "limit": 20,
  "offset": 0
}

Health Check

http
GET /api/v1/health

Ответ:

json
{
  "success": true,
  "data": {
    "status": "ok",
    "version": "1.0.0",
    "timestamp": 1703952000,
    "api": "web"
  }
}

Middleware

CORS

Настраивается через системную настройку ms3_cors_allowed_origins:

  • * — разрешить все домены
  • https://example.com,https://shop.example.com — список доменов

Rate Limiting

Защита от злоупотреблений через системные настройки:

  • ms3_rate_limit_max_attempts — максимум запросов (по умолчанию 60)
  • ms3_rate_limit_decay_seconds — период в секундах (по умолчанию 60)
  • ms3_rate_limit_store — хранилище счётчиков: file, redis, memcached (по умолчанию file)
  • ms3_rate_limit_storage_path — каталог для file (пусто = системный temp)
  • ms3_rate_limit_redis_dsn — DSN Redis (если задан, перекрывает host/port)
  • ms3_rate_limit_redis_host / ms3_rate_limit_redis_port / ms3_rate_limit_redis_password / ms3_rate_limit_redis_database
  • ms3_rate_limit_memcached_servers — серверы Memcached (по умолчанию 127.0.0.1:11211)

При превышении лимита возвращается:

json
{
  "success": false,
  "message": "Too many requests",
  "code": 429
}

SSR (Server-Side Rendering)

API поддерживает серверный рендеринг HTML для обновления частей страницы.

Использование

Передайте массив токенов сниппетов в параметре render:

javascript
fetch('/api/v1/cart/add?ms3_token=' + token, {
    method: 'POST',
    body: JSON.stringify({
        id: 123,
        render: ['ms3_abc123...', 'ms3_def456...']
    })
})

Ответ включает HTML:

json
{
  "success": true,
  "data": {
    "cart": [...],
    "status": {...},
    "render": {
      "ms3_abc123...": "<div class=\"cart\">...</div>",
      "ms3_def456...": "<span class=\"count\">5</span>"
    }
  }
}

Регистрация сниппетов

Токены генерируются автоматически при вызове сниппетов с параметром selector:

fenom
{'msCart' | snippet : [
    'tpl' => 'tpl.msCart',
    'selector' => '#cart-container'
]}

Кастомные роуты

Для добавления собственных endpoints создайте файл:

core/config/ms3_routes_web.custom.php

Пример:

php
<?php
use MiniShop3\Router\Response;

$router->group('/api/v1', function($router) use ($modx) {

    $router->get('/custom/endpoint', function($params) use ($modx) {
        return Response::success(['custom' => 'data']);
    });

});

Кастомные роуты загружаются после системных и могут их переопределять.

JavaScript клиент

MiniShop3 предоставляет JavaScript библиотеку для работы с API:

javascript
// Добавить в корзину
await ms3.cartAPI.add(123, 2, { color: 'red' })

// Оформить заказ
const result = await ms3.orderAPI.submit()

// Хуки
ms3Hooks.addHook('afterAddCart', async ({ response }) => {
  console.log('Товар добавлен', response.data)
})

Подробнее в разделе Frontend JavaScript.

Коды ошибок

КодОписание
400Неверный запрос (отсутствуют параметры, ошибка валидации)
401Требуется токен авторизации
403Доступ запрещён
404Ресурс не найден
429Превышен лимит запросов
500Внутренняя ошибка сервера

Отладка

Включите режим отладки через настройку ms3_api_debug:

json
{
  "success": false,
  "message": "Internal server error",
  "code": 500,
  "debug": {
    "exception": "Exception",
    "message": "Detailed error message",
    "file": "/path/to/file.php",
    "line": 123
  }
}

Безопасность

Не включайте режим отладки на продакшене — он раскрывает внутреннюю структуру приложения.