REST API - Общая архитектура Wepps Platform
REST API Wepps Platform — это система предоставления данных через HTTP endpoints. Состоит из версионированных API с разными подходами: M2M для машинных интеграций (1С, CRM), APP для мобильных приложений и фронтенда, wepps для админки, v0 для тестовых endpoints и cli для командной строки.
Структура REST API
Все REST-запросы обрабатываются единым фронт-контроллером Rest::Request.php:
┌─────────────────────────────────────────────────────────┐
│ HTTP Request с Bearer Token │
│ │
│ GET /rest/v1/goods │
│ POST /rest/v1/cart │
│ PUT /rest/m2m/goods.stocks │
│ DELETE /rest/m2m/orders │
└──────────────────┬────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Rest.php (фреймворк) │
│ parseRequest() → version, method, type │
│ routeRequest() → класс из RestConfig.php │
│ executeHandler() → валидация + auth + вызов метода │
│ │
│ /v1/goods → RestV1APP::getGoods() │
│ /m2m/users → RestV1M2M::getUsers() │
│ /wepps/token → RestAd::getToken() │
│ /cli/tasks → RestCli::tasksProcess() │
└──────────────┬────────────────────────────────────────┘
│
┌───────┴─────────┬──────────────┐
▼ ▼ ▼
RestV1APP RestV1M2M RestV1 / RestAd / RestCli
(Бизнес v1) (Generic m2m) (Auth/Profile/Admin/CLI)
Endpoint Endpoint Endpoint
│ │ │
└─► getHome() └─► fetch() └─► getToken()
└─► postCart() └─► addBatch() └─► getProfile()
└─► placeOrder() └─► setBatch() └─► tasksProcess()
└─► remove()
Авторизация
Bearer Token
Все REST endpoints требуют авторизацию через Bearer Token в заголовке:
GET /rest/v1/cart HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Токен содержит (payload JWT):
typ— тип токена:auth(доступ),refresh(обновление),confirm(подтверждение)id— ID пользователяexp— срок истечения
Типы токенов:
| Токен | typ | Срок жизни | Назначение |
|---|---|---|---|
| access | auth |
1 час (3600 с) | Аутентификация запросов |
| refresh | refresh |
30 дней (2592000 с) | Получение новой пары токенов |
| confirm | confirm |
10 минут (600 с) | Подтверждение входа кодом из письма |
Получение токена (v1)
POST /rest/v1/auth.login HTTP/1.1
Content-Type: application/json
{
"data": {
"login": "user@example.com",
"password": "password123"
}
}
Response:
{
"status": 200,
"message": "Login successful",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"access_exp": 1774567890,
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_exp": 1777159890
}
}
Обновление токена (v1):
POST /rest/v1/auth.refresh HTTP/1.1
Content-Type: application/json
{
"data": {
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}
}
💡 Режим CONFIRM_AUTH: если на сервере включён
CONFIRM_AUTH,auth.loginвозвращаетconfirm_token(типconfirm, 10 мин) и отправляет код на почту. Токены выдаются послеPOST /rest/v1/auth.confirmс телом{"data": {"token": "<confirm_token>", "code": 154566}}— полеcodeнеобязательно (для 2FA).
Токен для админки (wepps)
Админка получает токен через GET-параметры:
GET /rest/wepps/token?login=admin@example.com&password=secret HTTP/1.1
Response:
{
"status": 200,
"message": "Login successful",
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"exp": 1774567890
}
}
Токен для M2M (машинная интеграция)
M2M-эндпоинты требуют auth_required => true + role_required => [1], т.е. токен пользователя с правами UserPermissions = 1 (администратор M2M).
Авторизация общая с APP — токен получается через тот же POST /rest/v1/auth.login (в Bruno-коллекциях это auth/auth.login.bru, он сохраняет access_token/refresh_token в переменные окружения):
POST /rest/v1/auth.login HTTP/1.1
Content-Type: application/json
{
"data": {
"login": "m2m-admin@example.com",
"password": "secret"
}
}
Response:
{
"status": 200,
"message": "Login successful",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"access_exp": 1774567890,
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_exp": 1777159890
}
}
Полученный access_token передаётся в заголовке во всех M2M-запросах:
GET /rest/m2m/users?page=1&limit=20 HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Примечание: также подойдёт
GET /rest/wepps/token(способ админки) — он возвращает такой же токен. Главное, чтобы у учётной записи была роль 1 (UserPermissions = 1), иначе M2M вернёт403 Forbidden.
Как настраивается защита endpoints
В конфиге RestConfig.php для каждого endpoint можно указать:
| Ключ | Значение | Поведение |
|---|---|---|
auth_required |
true |
Запрос без токена → 401 |
auth_optional |
true |
Токен проверяется, но его отсутствие не блокирует запрос |
role_required |
[1, 2] |
Допустимые UserPermissions; иначе → 403 |
Проверка выполняется в Rest::executeHandler():
// Проверка аутентификации, если требуется (или если заданы роли)
if (!empty($config['auth_required']) || isset($config['role_required'])) {
$this->authenticateBearerToken();
} elseif (!empty($config['auth_optional'])) {
try {
$this->authenticateBearerToken();
} catch (\Exception $e) {
// токен опционален — игнорируем
}
}
// Проверка прав доступа по UserPermissions
if (isset($config['role_required'])) {
$userPermissions = (int) ($this->user['UserPermissions'] ?? 0);
if (!in_array($userPermissions, $config['role_required'], true)) {
throw new \Exception('Access denied: insufficient permissions', 403);
}
}
Конфигурация Endpoints (RestConfig.php)
Все endpoints регистрируются в едином реестре WeppsExtensions\Addons\Rest\RestConfig.php:
'v1' => [
'get' => [
'goods' => [
'class' => RestV1APP::class,
'method' => 'getGoods',
'note' => 'Список товаров с фильтрацией и пагинацией',
'query_validation' => [
'page' => ['type' => 'int2', 'required' => false],
'limit' => ['type' => 'int2', 'required' => false],
'sort' => ['type' => 'string', 'required' => false],
'search' => ['type' => 'string', 'required' => false],
'category' => ['type' => 'int2', 'required' => false],
],
],
'cart.metrics' => [
'class' => RestV1APP::class,
'method' => 'getCartMetrics',
'note' => 'Количество позиций корзины и их id',
'auth_optional' => true,
],
],
'post' => [
'auth.login' => [
'class' => RestV1::class,
'method' => 'postAuthLogin',
'note' => 'Аутентификация пользователя и выдача JWT-токенов',
'log' => false, // не логировать пароли
'validation' => [
'login' => ['type' => 'email', 'required' => true],
'password' => ['type' => 'string', 'required' => true],
],
],
'cart' => [
'class' => RestV1APP::class,
'method' => 'postCart',
'note' => 'Добавление товара в корзину',
'auth_required' => true,
'validation' => [
'id' => ['type' => 'string', 'required' => true],
'quantity' => ['type' => 'int2', 'required' => false],
],
],
],
// put / delete / cli — аналогично
],
Структура конфига:
[версия] => [
[http-метод: get|post|put|delete|cli] => [
[имя endpoint'а] => [
'class' => Класс-обработчик,
'method' => Метод обработчика,
'note' => Описание для документации,
// опционально:
'auth_required' => true,
'auth_optional' => true,
'role_required' => [1, 2],
'validation' => [...], // правила тела JSON
'query_validation' => [...], // правила GET-параметров
'custom_response' => true, // ответ без обёртки status/message/data
'log' => false, // отключить логирование
'async' => true, // поставить в очередь s_Tasks (202 Accepted)
],
],
],
Версионирование API
REST API Wepps поддерживает несколько версий одновременно:
v1 — Мобильное приложение и фронтенд
Endpoints:
/rest/v1/home— главный экран приложения/rest/v1/goods— каталог товаров/rest/v1/cart— операции с корзиной/rest/v1/orders— заказы пользователя/rest/v1/auth.login— аутентификация
Особенности:
- ✅ Бизнес-ориентированные endpoints
- ✅ camelCase ответы
- ✅ Auth: access + refresh токены
- ✅ Опциональная авторизация (
auth_optional) для публичных данных
m2m — Generic CRUD
Endpoints (явные методы на каждую таблицу):
/rest/m2m/users— CRUD пользователей/rest/m2m/goods— CRUD товаров (+goods.navigator,goods.statuses,goods.attributes,goods.variations,goods.stocks)/rest/m2m/orders— CRUD заказов/rest/m2m/files— CRUD файлов (загрузка base64/url)/rest/m2m/tasks.result— результат async-задачи
Особенности:
- ✅ Универсальный CRUD через один
RestV1M2M+RestV1M2MUtils - ✅ Batch-операции: одиночная запись → 201, массив (до 100) → 207 Multi-Status
- ✅ Валидация из БД (
s_ConfigFields), кэш в Memcached - ✅ Перед/после-колбэки (setBefore/setAfter) для бизнес-логики
- ✅
asyncрежим для тяжёлых операций (202 Accepted → s_Tasks)
wepps — Админка
Endpoints:
/rest/wepps/token— получение JWT-токена админки/rest/wepps/list_items— поиск по спискам админки (custom_response)
Особенности:
- ✅ Требует
ShowAdmin = 1у пользователя - ✅ Токен для внутренних AJAX-запросов админки
v0 — Тестовые endpoints
Endpoints:
/rest/v0/test— тестовая валидация всех типов (int, float, email, phone, guid, barcode)
cli — Командная строка
Endpoints:
php Request.php tasks.process— обработка async-задач из s_Tasksphp Request.php tasks.result— результат задачиphp Request.php removeLogLocal— очистка логов
Формат ответа
Все endpoints возвращают стандартный JSON envelope:
{
"status": 200,
"message": "OK",
"data": { ... }
}
Дополнительные поля (например pagination) добавляются как соседние ключи:
{
"status": 200,
"message": "OK",
"data": [ ... ],
"pagination": {
"count": 120,
"limit": 20,
"page": 1
}
}
Статус коды
| Код | Значение | Пример |
|---|---|---|
| 200 | OK | Успешный запрос |
| 201 | Created | Ресурс создан (одиночная запись) |
| 202 | Accepted | Запрос поставлен в async-очередь (s_Tasks) |
| 207 | Multi-Status | Batch-операция с per-item статусами |
| 400 | Bad Request | Ошибка валидации |
| 401 | Unauthorized | Требуется авторизация / неверный токен |
| 403 | Forbidden | Недостаточно прав (role_required) |
| 404 | Not Found | Ресурс не найден |
| 409 | Conflict | Дубликат уникального ключа |
| 429 | Too Many Requests | Превышен лимит запросов |
| 500 | Server Error | Ошибка сервера |
Особенности ответов
- Для v1 (APP) ключи данных автоматически конвертируются из PascalCase БД в camelCase (
OStatus→status,OSum→sum). - Для m2m данные приходят с camelCase маппингом из
s_ConfigFields.ApiMapping. - Для custom_response endpoints ответ отправляется без обёртки
status/message/data.
Примеры ответов
Успех (200):
{
"status": 200,
"message": "OK",
"data": [
{ "id": 1, "name": "Product 1", "price": 100 },
{ "id": 2, "name": "Product 2", "price": 200 }
]
}
Ошибка валидации (400):
{
"status": 400,
"message": "Validation error",
"data": {
"id": "Field is required",
"quantity": "Must be at least 1"
}
}
Ошибка авторизации (401):
{
"status": 401,
"message": "Unauthorized",
"data": null
}
Ошибка прав (403):
{
"status": 403,
"message": "Forbidden",
"data": null
}
Ресурс не найден (404):
{
"status": 404,
"message": "Product not found",
"data": null
}
Валидация запросов
Валидация происходит в двух местах:
1. Config-level валидация (RestConfig.php)
Выполняется в Rest::validateData() перед вызовом метода:
'validation' => [
'id' => ['type' => 'string', 'required' => true],
'quantity' => ['type' => 'int2', 'required' => false],
],
Поддерживаемые типы:
| Тип | Проверка | Пример |
|---|---|---|
int |
Целое число (strict) | 42 (не "42") |
int2 |
Целое число или строка с числом | 42, "100" |
float |
Число с плавающей точкой (strict) | 3.14 |
float2 |
Число или строка с числом | 3.14, "2.5" |
string |
Любая строка | "hello" |
email |
Валидный email адрес | "user@example.com" |
date |
Валидная дата (strtotime) | "2026-04-07" |
phone |
Телефон (10 цифр) | "9123456789" |
guid |
GUID формат UUID | "550e8400-e29b-41d4-a716-446655440000" |
barcode |
Штрих-код EAN13 | "1234567890128" |
object |
Ассоциативный массив | {"a": 1} |
int[], string[] и т.д. |
Массив элементов указанного типа | [1, 2, 3] |
Вложенная валидация (dot-notation):
'validation' => [
'data' => ['type' => 'object', 'required' => true],
'data.items[]' => ['type' => 'object[]', 'required' => true],
'data.items[].id' => ['type' => 'int', 'required' => true],
'data.items[].name' => ['type' => 'string', 'required' => true],
'data.items[].sum' => ['type' => 'float2', 'required' => true],
],
Валидация также проверяет лишние поля — любое поле, не описанное в правилах, вызывает ошибку Unexpected field '...'.
2. Business-logic валидация (в методе)
Сложная бизнес-логика, которую нельзя описать в конфиге:
public function postCart($data = null): array
{
$id = trim($data['data']['id'] ?? '');
// Config валидация уже прошла, данные безопасны
// Теперь проверяем бизнес-логику
$productsUtils = new ProductsUtils();
$validation = $productsUtils->validateProductId($id);
if (!$validation['exists']) {
return ['status' => 404, 'message' => $validation['message'], 'data' => null];
}
// Товар существует, можно добавлять в корзину
...
}
Request Format
Все POST/PUT/DELETE запросы используют JSON со структурой:
{
"data": {
"id": "325",
"quantity": 2,
"active": 1
}
}
Поле data — содержит данные запроса. Разворачивается в RestV1M2M::normalizeInput() для batch-операций:
{
"data": [
{ "guid": "...", "name": "Товар 1", "price": "100.00" },
{ "guid": "...", "name": "Товар 2", "price": "200.00" }
]
}
Поле type — раньше использовалось для указания endpoint'а; сейчас не требуется.
Response Envelope
{
"status": <HTTP Status Code>,
"message": <Human-readable message>,
"data": <Payload> | <Errors> | null
}
status — HTTP статус код (200, 400, 404 и т.д.)
message — человеческое сообщение ("OK", "Validation error", "Not found")
data — в зависимости от типа ответа:
- ✅ Успех: массив данных или объект
- ❌ Ошибка валидации: объект ошибок по полям
- ❌ Ошибка:
null
Лучшие практики
1. Валидация — в конфиге, в методе — только бизнес-логика
В Wepps проверка полей (тип, обязательность, лишние поля) выполняется автоматически на уровне конфига в RestConfig.php — метод получает уже провалидированные данные:
// RestConfig.php — декларативные правила валидации
'cart' => [
'validation' => [
'id' => ['type' => 'string', 'required' => true],
'quantity' => ['type' => 'int2', 'required' => false],
],
],
// ❌ Плохо — дублировать проверку, которую уже сделал фреймворк
$id = trim($data['id'] ?? '');
if (empty($id)) {
return ['status' => 400, 'message' => 'ID required', 'data' => null];
}
// ✅ Хорошо — конфиг уже проверил тип и обязательность,
// в методе просто берём готовое значение
$records = $this->normalizeInput(); // разворачивает {data: ...} → [запись]
$id = $records[0]['id']; // id гарантирован есть (required = true)
$quantity = $records[0]['quantity'] ?? 1; // необязательное поле — с дефолтом
В методе проверяйте только то, что невозможно описать в конфиге: существование записи в БД, права доступа, бизнес-состояния (см. Business-logic валидация).
2. Использовать стандартные HTTP коды
// ❌ Плохо
return ['status' => 200, 'message' => 'Product not found'];
// ✅ Хорошо
return ['status' => 404, 'message' => 'Product not found', 'data' => null];
3. Возвращать структурированные ошибки
// ❌ Плохо
return ['status' => 400, 'message' => 'Validation error: id is required, quantity must be > 0'];
// ✅ Хорошо
return [
'status' => 400,
'message' => 'Validation error',
'data' => [
'id' => 'Field is required',
'quantity' => 'Must be greater than 0',
],
];
4. Использовать guard clause (ранний return)
// ❌ Глубокая вложенность
if ($condition) {
// много кода
} else {
return ['status' => 400, 'message' => 'Error', 'data' => null];
}
// ✅ Guard clause
if (!$condition) {
return ['status' => 400, 'message' => 'Error', 'data' => null];
}
// основной код
5. Не выдавать внутренние ошибки клиенту
// ❌ Плохо
try {
$result = $database->query($sql);
} catch (Exception $e) {
return ['status' => 500, 'message' => $e->getMessage()]; // Утечка информации!
}
// ✅ Хорошо
try {
$result = $database->query($sql);
} catch (Exception $e) {
return ['status' => 500, 'message' => 'Server error', 'data' => null];
}
Async-режим (очередь задач)
Тяжёлые операции (например, массовое создание товаров из 1С) можно поставить в очередь:
'goods' => [
'class' => RestV1M2M::class,
'method' => 'postGoods',
'async' => true, // ← поставить в очередь s_Tasks
...
],
Поведение:
- Запрос получает ответ 202 Accepted с
task_id - Задача сохраняется в таблицу
s_Tasks(класс, метод, данные, пользователь) - Обработка выполняется CLI-скриптом:
php Request.php tasks.process - Результат получается по
GET /rest/m2m/tasks.result?id={task_id}
// 202 Accepted
{
"status": 202,
"message": "Accepted",
"data": { "task_id": 123 }
}
// GET /rest/m2m/tasks.result?id=123
// Пока задача в очереди:
{
"status": 200,
"message": "OK",
"data": {
"id": 123,
"is_processed": false, // ещё не выполнена
"http_status": null, // нет результата
"response": null
}
}
// После выполнения:
{
"status": 200,
"message": "OK",
"data": {
"id": 123,
"is_processed": true,
"http_status": 201, // статус исходной операции
"response": {
"status": 201,
"data": { "id": 321 } // результат операции
}
}
}
Polling: опрашивайте GET /rest/m2m/tasks.result?id={task_id} с интервалом (например, раз в 5–10 секунд), пока is_processed не станет true. Если задачи с таким id нет — вернётся 404.
Интеграция с внешними системами
Для интеграции с внешними системами используйте RestV1M2M (/rest/m2m/). Все M2M-эндпоинты требуют Bearer-токен пользователя с ролью 1 (s_Users.UserPermissions = 1).
Шаг 1 — получить токен через общую авторизацию POST /rest/v1/auth.login (см. Токен для M2M):
POST /rest/v1/auth.login HTTP/1.1
Content-Type: application/json
{
"data": {
"login": "m2m-admin@example.com",
"password": "secret"
}
}
Шаг 2 — использовать токен во всех запросах:
# Создать товар (batch из 1С)
POST /rest/m2m/goods HTTP/1.1
Authorization: Bearer token...
{
"data": [
{
"guid": "550e8400-e29b-41d4-a716-446655440000",
"name": "Свитер",
"alias": "sviter",
"navigatorId": "8",
"price": "17450.00"
}
]
}
# Обновить остатки товара
PUT /rest/m2m/goods.stocks HTTP/1.1
Authorization: Bearer token...
{
"data": [
{ "id": 1, "stocks": 5 },
{ "id": 2, "stocks": 0 }
]
}
# Удалить заказы (batch)
DELETE /rest/m2m/orders HTTP/1.1
Authorization: Bearer token...
{
"data": [5001, 5002, 5003]
}
Краткий путеводитель
Для мобильного приложения (v1):
- Используйте
/rest/v1/endpoints (RestV1APP, RestV1) - Примеры:
/rest/v1/home,/rest/v1/goods,/rest/v1/cart,/rest/v1/auth.login - Готовая бизнес-логика, camelCase ответы, авторизация JWT
Для внешней интеграции (m2m):
- Используйте
/rest/m2m/endpoints (RestV1M2M) - Универсальный CRUD для таблиц: users, goods, orders, files, goods.variations, goods.stocks и т.д.
- Batch-операции (207 Multi-Status), валидация из БД (
s_ConfigFields) - Требует роль 1 (администратор M2M)
Для админки (wepps):
- Используйте
/rest/wepps/endpoints (RestAd) - Токен, поиск по спискам админки
Для командной строки (cli):
php Request.php tasks.process— обработка async-задачphp Request.php removeLogLocal— очистка логов