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

API заказа

Программный интерфейс для работы с заказами MiniShop3 из PHP-кода.

Заказ в MiniShop3 состоит из нескольких моделей:

  • msOrder — основная модель заказа (стоимость, статус, доставка, оплата)
  • msOrderAddress — адрес и контактные данные заказа
  • msOrderProduct — позиции (товары) в заказе
  • msOrderLog — журнал изменений заказа

Жизненный цикл заказа: черновик (draft) → оформление (submit) → смена статусов.

Контроллер Order (фасад)

Основной способ работы с заказами из PHP — контроллер-фасад Order. Он делегирует логику в специализированные сервисы, но предоставляет единый интерфейс.

php
$ms3 = $modx->services->get('ms3');

// Получить данные текущего заказа
$data = $ms3->order->get();

// Добавить поле
$result = $ms3->order->add('email', 'user@example.com');

// Установить несколько полей
$result = $ms3->order->set([
    'email' => 'user@example.com',
    'phone' => '+79991234567',
    'first_name' => 'Иван',
    'delivery_id' => 1,
    'payment_id' => 1,
    'order_comment' => 'Позвоните перед доставкой',
]);

// Оформить заказ
$result = $ms3->order->submit();
// ['success' => true, 'data' => ['order_id' => 15, 'order_num' => '26/02-15', ...]]

// Очистить черновик
$ms3->order->clean();

Инициализация

Перед использованием контроллера необходима инициализация с токеном клиента:

php
$ms3->order->initialize($token);
$ms3->order->initDraft();

В контексте REST API и сниппетов это происходит автоматически.

Методы контроллера

МетодОписание
initialize($token, $config)Инициализация с токеном клиента
initDraft()Загрузка существующего черновика
get()Данные заказа (поля + адрес)
add($key, $value)Добавить/обновить поле
set($fields)Установить несколько полей
remove($key)Удалить поле (сбросить в null)
validate($key, $value)Валидация значения поля
submit($data)Оформить заказ
clean()Очистить черновик
getCost($onlyCost)Полная стоимость (корзина + доставка + оплата)
getCartCost()Стоимость корзины
getDeliveryCost()Стоимость доставки
getPaymentCost()Комиссия оплаты
setCustomerAddress($hash)Применить сохранённый адрес клиента
cleanCustomerAddress()Очистить адресные поля
getDeliveryValidationRules($deliveryId)Правила валидации доставки
getDeliveryRequiresFields($deliveryId)Обязательные поля доставки
getDraft()Получить объект черновика msOrder

Черновики (OrderDraftManager)

Черновик — это объект msOrder со статусом draft. Он создаётся при первом взаимодействии клиента с корзиной и хранит данные до оформления.

php
$draftManager = $modx->services->get('ms3_order_draft_manager');

// Получить или создать черновик
$draft = $draftManager->getOrCreateDraft($token, 'web');

// Получить существующий черновик (без создания)
$draft = $draftManager->getDraft($token, 'web');

// Получить черновик по ID клиента (для восстановления после логина)
$draft = $draftManager->getDraftByCustomer($customerId, 'web');

// Данные черновика как массив (заказ + адрес)
$data = $draftManager->toArray($draft);
// Адресные поля возвращаются с префиксом address_:
// ['email' => '...', 'address_city' => 'Москва', 'address_street' => '...']

// Обновить одно поле
$draftManager->updateField($draft, 'order_comment', 'Позвоните перед доставкой');

// Привязать клиента к черновику
$draftManager->attachCustomer($draft, $customerId);

// Пересчитать стоимость
$draftManager->recalculate($draft);

// Установить стоимость доставки
$draftManager->setDeliveryCost($draft, 350.00);

// Проверить, пуста ли корзина
if ($draftManager->isEmpty($draft)) {
    // Нет товаров
}

// Очистить все поля
$draftManager->clean($draft);

// Удалить черновик полностью (с товарами и адресом)
$draftManager->deleteDraft($draft);

Поля и валидация (OrderFieldManager)

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

php
$fieldManager = $modx->services->get('ms3_order_field_manager');

// Добавить поле (с валидацией и событиями)
$result = $fieldManager->add($draft, $orderData, 'email', 'user@example.com');
// ['success' => true, 'data' => [...]]

// Удалить поле
$fieldManager->remove($draft, $orderData, 'email');

// Валидация без сохранения
$result = $fieldManager->validate($orderData, 'phone', '+79991234567');
// ['success' => true] или ['success' => false, 'message' => 'Ошибка']

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

По умолчанию валидируются delivery_id и payment_id как required|numeric. Дополнительные правила загружаются из настроек доставки.

php
// Получить правила валидации для доставки
$rules = $fieldManager->getDeliveryValidationRules($deliveryId);
// ['city' => 'required|min:2', 'street' => 'required', ...]

// Получить список обязательных полей
$required = $fieldManager->getDeliveryRequiredFields($deliveryId);
// ['city', 'street', 'building', 'phone']

// Добавить свои правила
$fieldManager->setValidationRules([
    'company_name' => 'required|min:3',
]);

Расчёт стоимости (OrderCostCalculator)

php
$calculator = $modx->services->get('ms3_order_cost_calculator');

// Стоимость корзины
$result = $calculator->getCartCost($draft, $token);
// ['cost' => 5000.00]

// Стоимость доставки
$result = $calculator->getDeliveryCost($draft, $orderData, $token);
// ['cost' => 300.00]

// Комиссия оплаты
$result = $calculator->getPaymentCost($draft, $orderData, $token);
// ['cost' => 150.00]

// Полная стоимость
$result = $calculator->getTotalCost($draft, $orderData, $token);
// ['cost' => 5450.00, 'cart_cost' => 5000.00, 'delivery_cost' => 300.00, 'payment_cost' => 150.00]

Каждый метод вызывает пару событий msOnBefore... / msOn..., позволяющих плагинам модифицировать стоимость.

Пересчёт стоимости в админке — POST /api/mgr/orders/{id}/recalculate-cost

Появился в 1.11.0. Сервис ManagerOrderCostRecalculator пересчитывает cart_cost, weight, delivery_cost, итоговый cost по сохранённым позициям заказа и текущим delivery_id / payment_id без побочных эффектов на других полях.

Тело запроса:

json
{
  "mode": "auto",
  "manual_delivery_cost": 500.0
}

Режимы (mode):

РежимПоведение
auto (по умолчанию)Пересчитывает только для DefaultDelivery / DefaultPayment (по полям price, weight_price, free_delivery_amount, проценты). Для кастомных handler'ов возвращает warning delivery_manual_required / payment_manual_required и сохраняет прежнюю delivery_cost / комиссию 0 — не дёргает внешние API.
manualИспользует переданный manual_delivery_cost. Комиссия оплаты считается по полю автоматом.
force_providerЯвно вызывает loadController()getCost() и loadHandler()getCost() в try/catch. При сбое — warning delivery_provider_error / payment_provider_error, прежние значения сохраняются.

Ответ содержит данные заказа (как GET /api/mgr/orders/{id}) плюс:

json
{
  "breakdown": {
    "cart_cost": 5000.0,
    "weight": 1.5,
    "delivery_cost": 300.0,
    "payment_cost": 150.0,
    "cost": 5450.0
  },
  "warnings": ["delivery_manual_required"]
}

Применяет общий guard OrderService::clampComputedTotal() — итог не может уйти ниже нуля. Скидки/наценки доставки и оплаты (отрицательные price, см. 1.11.0) обрабатываются через MiniShop3\Utils\PriceAdjustment.

Пример вызова из JS (карточка заказа в mgr):

javascript
const response = await fetch(`/api/mgr/orders/${orderId}/recalculate-cost`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'modAuth': MODx.modxConfig.auth, // или актуальный mgr-auth заголовок
  },
  body: JSON.stringify({
    mode: 'manual',
    manual_delivery_cost: 500,
  }),
})
const json = await response.json()
// json.data.breakdown — cart_cost / delivery_cost / payment_cost / cost
// json.data.warnings — например delivery_manual_required

Через HTTP (REST mgr API):

bash
curl -X POST 'https://example.com/api/mgr/orders/42/recalculate-cost' \
  -H 'Content-Type: application/json' \
  -H 'Cookie: …' \
  -d '{"mode":"auto"}'

См. также routing.

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

OrderSubmitHandler выполняет полный цикл оформления: валидация → создание клиента → расчёт стоимости → генерация номера → смена статуса → вызов оплаты.

php
$submitHandler = $modx->services->get('ms3_order_submit_handler');

$result = $submitHandler->submit($draft, $orderData, $token);
// Успех:
// [
//     'success' => true,
//     'data' => [
//         'order_id' => 15,
//         'order_num' => '26/02-15',
//         'redirect_url' => '/thank-you?msorder=15',
//     ],
//     'message' => 'Заказ успешно оформлен'
// ]

Порядок действий при оформлении

  1. Событие msOnSubmitOrder
  2. Проверка: корзина не пуста
  3. Валидация delivery_id, payment_id и обязательных полей доставки
  4. Поиск/создание клиента (msCustomer)
  5. Опционально: создание пользователя MODX (настройка ms3_order_register_user_on_submit)
  6. Расчёт стоимости (корзина + доставка + оплата)
  7. Генерация номера заказа
  8. Сохранение адреса клиента
  9. События msOnBeforeCreateOrder / msOnCreateOrder
  10. Смена статуса на ms3_status_new (по умолчанию: 2)
  11. Вызов $msPayment->send() — редирект на оплату
  12. Возврат URL страницы «Спасибо»

Генерация номера заказа

php
$num = $submitHandler->getNewOrderNum();
// "26/02-15" — формат настраивается:
// ms3_order_format_num — формат даты (по умолчанию 'ym')
// ms3_order_format_num_separator — разделитель (по умолчанию '/')

Управление статусами (OrderStatusService)

php
$statusService = $modx->services->get('ms3_order_status');

// Сменить статус заказа
$result = $statusService->change($orderId, $newStatusId);
// true — успех
// string — текст ошибки из лексикона

// Сменить статус без уведомлений
$result = $statusService->change($orderId, $newStatusId, true);

Ограничения статусов

Свойство статусаПоведение
final = trueНельзя сменить на другой статус
fixed = trueМожно переключить только на статус с большей position

Уведомления

При смене статуса автоматически отправляются уведомления через NotificationManager:

  • Клиенту — email, телефон из msCustomer или modUserProfile
  • Менеджерам — из настроек ms3_email_manager, ms3_phone_manager, ms3_telegram_manager

Позиции заказа (msOrderProduct)

Товары в заказе хранятся в модели msOrderProduct (таблица ms3_order_products).

Поля msOrderProduct

ПолеТипОписание
product_idintegerID товара (msProduct)
order_idintegerID заказа
product_keystringУникальный ключ позиции (напр. 123_a1b2c3d4)
namestringНазвание товара
countintegerКоличество
pricefloatЦена за единицу
weightfloatВес за единицу
costfloatСтоимость позиции (price × count)
optionsjsonВыбранные опции
propertiesjsonДополнительные свойства

Программная работа с позициями

php
use MiniShop3\Model\msOrder;
use MiniShop3\Model\msOrderProduct;

$order = $modx->getObject(msOrder::class, $orderId);

// Получить все позиции заказа
$products = $order->getMany('Products');
foreach ($products as $product) {
    echo $product->get('name') . ': ' . $product->get('count') . ' × ' . $product->get('price');
}

// Добавить позицию
$item = $modx->newObject(msOrderProduct::class);
$item->set('order_id', $orderId);
$item->set('product_id', $productId);
$item->set('product_key', $productId . '_' . md5(json_encode($options)));
$item->set('name', 'Товар');
$item->set('count', 2);
$item->set('price', 1500.00);
$item->set('cost', 3000.00);
$item->set('weight', 0.5);
$item->set('options', $options);
$item->save();

// Пересчитать итоги заказа после изменения позиций
$order->updateProducts();

Пересчёт итогов

После добавления, удаления или изменения позиций вызывайте $order->updateProducts(). Метод пересчитывает cart_cost, weight и cost на основе всех msOrderProduct.

Адрес заказа (msOrderAddress)

Каждый заказ имеет один связанный объект msOrderAddress (таблица ms3_order_addresses).

Поля msOrderAddress

ПолеТипОписание
order_idintegerID заказа
first_namestringИмя
last_namestringФамилия
phonestringТелефон
emailstringEmail
countrystringСтрана
indexstringПочтовый индекс
regionstringРегион
citystringГород
metrostringСтанция метро
streetstringУлица
buildingstringДом
entrancestringПодъезд
floorstringЭтаж
roomstringКвартира/офис
commentstringКомментарий к адресу
text_addressstringПолный адрес одной строкой
propertiesjsonДополнительные свойства

Программная работа

php
use MiniShop3\Model\msOrder;

$order = $modx->getObject(msOrder::class, $orderId);
$address = $order->getOne('Address');

// Чтение
echo $address->get('city');       // "Москва"
echo $address->get('street');     // "Ленина"

// Обновление
$address->set('city', 'Санкт-Петербург');
$address->save();

OrderAddressManager

Сервис для работы с адресами в контексте черновика:

php
$addressManager = $modx->services->get('ms3_order_address_manager');

// Применить сохранённый адрес клиента к черновику
$result = $addressManager->setCustomerAddress($draft, $orderData, $addressHash);

// Очистить все адресные поля
$result = $addressManager->cleanCustomerAddress($draft, $orderData);

// Сохранить адрес заказа в адреса клиента
$savedAddress = $addressManager->saveToCustomerAddresses($customerId, $orderData);

Журнал заказа (OrderLogService)

Журнал фиксирует все изменения заказа: смену статуса, изменение полей, работу с позициями.

php
$logService = $modx->services->get('ms3_order_log');

// Добавить запись в журнал
$logService->addEntry($orderId, 'status', [
    'old' => 1,
    'new' => 2,
]);

// Добавить запись о произвольном действии
$logService->addEntry($orderId, 'field', [
    'key' => 'delivery_id',
    'old_value' => 1,
    'new_value' => 2,
], true);  // visible = true (видна клиенту)

// Получить записи журнала
$entries = $logService->getEntries($orderId);
// Только видимые клиенту
$entries = $logService->getEntries($orderId, true);
// С лимитом
$entries = $logService->getEntries($orderId, false, 50);

// Проверить, логируется ли действие (настройка ms3_order_log_actions)
if ($logService->shouldLog('status')) {
    // ...
}

Типы действий

КонстантаЗначениеОписание
msOrderLog::ACTION_STATUSstatusСмена статуса
msOrderLog::ACTION_PAYMENTpaymentИзменение оплаты
msOrderLog::ACTION_PRODUCTSproductsИзменение позиций
msOrderLog::ACTION_ADDRESSaddressИзменение адреса
msOrderLog::ACTION_FIELDfieldИзменение поля заказа

Настройка ms3_order_log_actions определяет, какие действия логируются (по умолчанию: status,products,field,address; значение * для логирования всех действий).

Финализация из менеджера (OrderFinalizeService)

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

php
use MiniShop3\Services\Order\OrderOrigin;

$finalizeService = $modx->services->get('ms3_order_finalize');

$result = $finalizeService->finalize($orderId, [
    'skip_validation' => false,
    'skip_notifications' => false,
    'create_customer' => true,
    'force_create_customer' => false,
    'origin' => OrderOrigin::MANAGER, // по умолчанию; для CRM — OrderOrigin::INTEGRATION
]);

Финализация допускает skip_* и работает с уже существующим черновиком. Вызова платёжного gateway здесь нет (в отличие от storefront submit).

Параметр origin (OrderOrigin): manager (по умолчанию), storefront, integration. При manager дополнительно вызываются msOnBeforeMgrCreateOrder / msOnMgrCreateOrder. Ключ from_manager в событиях = true только для origin=manager.

Программное создание заказа (ProgrammaticOrderService)

API без HTTP-сессии для extras, cron и интеграций. Это не Web API: в routes/web.php отдельного эндпоинта нет.

DIms3_programmatic_order
КлассMiniShop3\Services\Order\ProgrammaticOrderService
ЧерновикOrderDraftManager::createSessionlessDraft() (без PHP-сессии и cart token)
ФинализацияOrderFinalizeService::finalize(..., origin=integration)
Идемпотентностьколонка ms3_orders.idempotency_key (unique, nullable)
php
use MiniShop3\Services\Order\OrderOrigin;

$orders = $modx->services->get('ms3_programmatic_order');

$result = $orders->create([
    'idempotency_key' => 'crm-invoice-10042', // обязательно
    'products' => [
        ['product_id' => 15, 'count' => 2],
        // или снимок без ресурса:
        // ['name' => 'Услуга', 'price' => 500, 'count' => 1, 'weight' => 0, 'options' => []],
    ],
    'customer_id' => 0,
    'delivery_id' => 1,
    'payment_id' => 1,
    'address' => [
        'first_name' => 'Иван',
        'email' => 'user@example.com',
        'phone' => '+79991234567',
    ],
    'order_comment' => 'Из CRM',
    'context' => 'web',
    'origin' => OrderOrigin::INTEGRATION,
    // 'delivery_cost' => 300, // → cost_mode=manual при finalize
    // 'skip_notifications' => true,
    // 'skip_validation' => false,
    // 'properties' => ['source' => 'crm'],
]);

if (!$result['success']) {
    // Частые message:
    // ms3_order_err_idempotency_key_required
    // ms3_order_err_products_required
    // ms3_order_err_programmatic_create
    // либо ошибки finalize / валидации
    return $result;
}

// Успех: data = { order_id, uuid, num, status_id }
// Повтор того же idempotency_key:
//   уже оформленный заказ → success + ms3_order_programmatic_idempotent
//   черновик со статусом draft → повторная финализация

События: msOnBeforeCreateOrder / msOnCreateOrder с origin=integration и без from_manager. События msOnBeforeMgrCreateOrder / msOnMgrCreateOrder не вызываются.

Отличие от POST /api/mgr/orders (менеджер создаёт пустой/частичный заказ в UI) и от storefront POST /api/v1/order/submit (нужны токен и корзина).

Разрешение пользователей (OrderUserResolver)

Сервис создаёт или находит пользователя MODX по данным заказа.

php
$userResolver = $modx->services->get('ms3_order_user_resolver');

// Получить или создать MODX user_id
$userId = $userResolver->getUserId($orderData);

// Проверить существование пользователя
$user = $userResolver->checkUserExists([
    'email' => 'user@example.com',
    'phone' => '+79991234567',
]);

// Создать пользователя
$user = $userResolver->createUser([
    'email' => 'user@example.com',
    'first_name' => 'Иван',
    'last_name' => 'Иванов',
    'phone' => '+79991234567',
]);

Настройка ms3_order_user_groups определяет группы для новых пользователей (формат: group_id:role_id, через запятую).

Поля msOrder

ПолеТипПо умолчаниюОписание
user_idinteger0ID пользователя MODX
customer_idinteger0ID клиента (msCustomer)
tokenstringТокен сессии клиента
uuidstringУникальный UUID заказа
createdondatetimenullДата создания
updatedondatetimenullДата обновления
numstring''Номер заказа
costfloat0.0Итоговая стоимость
cart_costfloat0.0Стоимость товаров
delivery_costfloat0.0Стоимость доставки
weightfloat0.0Общий вес
status_idinteger0ID текущего статуса
delivery_idinteger0ID способа доставки
payment_idinteger0ID способа оплаты
contextstring'web'Контекст MODX
order_commentstringnullКомментарий к заказу
idempotency_keystringnullКлюч идемпотентности (программные заказы, unique)
propertiesjsonnullДополнительные свойства (в т.ч. origin для интеграций)

Связи msOrder

СвязьМодельТипОписание
AddressmsOrderAddresscomposite (one)Адрес заказа
ProductsmsOrderProductcomposite (many)Позиции заказа
LogmsOrderLogcomposite (many)Журнал изменений
CustomermsCustomeraggregateКлиент
StatusmsOrderStatusaggregateСтатус
DeliverymsDeliveryaggregateСпособ доставки
PaymentmsPaymentaggregateСпособ оплаты
UsermodUseraggregateПользователь MODX

Composite vs Aggregate

Composite-связи удаляются каскадно при удалении заказа (адрес, позиции, журнал). Aggregate-связи — только ссылки, связанные объекты не удаляются.

События

СобытиеКогда вызывается
msOnBeforeSaveOrder / msOnSaveOrderСохранение заказа
msOnBeforeRemoveOrder / msOnRemoveOrderУдаление заказа
msOnBeforeGetCartCost / msOnGetCartCostРасчёт стоимости корзины
msOnBeforeGetDeliveryCost / msOnGetDeliveryCostРасчёт стоимости доставки
msOnBeforeGetPaymentCost / msOnGetPaymentCostРасчёт комиссии оплаты
msOnBeforeAddToOrder / msOnAddToOrderДобавление/изменение поля
msOnBeforeRemoveFromOrder / msOnRemoveFromOrderУдаление поля
msOnBeforeValidateOrderValue / msOnValidateOrderValueВалидация значения
msOnErrorValidateOrderValueОшибка валидации
msOnSubmitOrderНачало оформления
msOnBeforeCreateOrder / msOnCreateOrderСоздание заказа
msOnBeforeChangeOrderStatus / msOnChangeOrderStatusСмена статуса
msOnBeforeGetOrderUser / msOnGetOrderUserПоиск/создание пользователя
msOnBeforeMgrCreateOrder / msOnMgrCreateOrderФинализация из менеджера (только origin=manager)

Manager REST API

Эндпоинты для Vue-интерфейса заказов (сессия mgr, см. routing):

МетодПутьОписание
GET/api/mgr/orders/statsАгрегаты для фильтров и дашборда
POST/api/mgr/ordersСоздание заказа из менеджера
POST/api/mgr/orders/{id}/finalizeФинализация черновика
POST/api/mgr/orders/{id}/recalculate-costПересчёт через ManagerOrderCostRecalculator

Создание и финализация из mgr проходят через OrderFinalizeService и события msOnBeforeMgrCreateOrder / msOnMgrCreateOrder.

Подробное описание параметров событий — в разделе События.