
Способы доставки

Откройте Пакеты → MiniShop3 → Настройки → Варианты доставки.
Поля доставки
| Поле | Тип | Описание |
|---|---|---|
name | string | Название способа доставки |
description | text | Описание для покупателя |
price | string | Доплата за доставку: фиксированная сумма, процент от стоимости корзины (5%) или отрицательное значение-скидка. Допустимый процент — от −100 до 100, иначе доплата обнуляется с записью в журнал. В интерфейсе поле подписано «Доп. стоимость». В БД — varchar(11) |
weight_price | float | Стоимость за единицу веса |
distance_price | float | Стоимость за единицу расстояния — в расчёте сейчас не используется (резервное поле) |
free_delivery_amount | float | Сумма заказа для бесплатной доставки |
logo | string | Путь к изображению |
position | tinyint unsigned | Порядок сортировки. В карте модели колонка объявлена как tinyint(1) unsigned, то есть предел — 255 записей. У способов оплаты та же колонка int, это историческое расхождение |
active | bool | Активность |
class | string | PHP-класс обработчика |
validation_rules | JSON | Правила валидации полей |
properties | JSON | JSON-свойства для своих данных обработчика |
Диалог доставки
Форма разбита на вкладки:
- Доставка — название, описание, логотип, активность;
- Настройки — класс обработчика, доплата, цена за вес, порог бесплатной доставки, правила валидации;
- Способы оплаты — переключатели связей. Вкладка появляется только при правке: у доставки без сохранения нет id, к которому привязывать оплату.
Связи пишутся сразу по переключению, через POST и DELETE /api/mgr/deliveries/{id}/payments, и не ждут кнопки сохранения.
Связь с оплатой
Каждый способ доставки связываете с нужными способами оплаты.
- Наличные только для самовывоза
- Онлайн-оплата для курьерской доставки
- Свои комбинации для разных регионов
Когда покупатель выбирает доставку, список оплат фильтруется.
Расчёт стоимости
Стоимость доставки рассчитывается по формуле:
Итоговая стоимость = weight_price × вес + (price как сумма или процент от корзины)Вес заказа — сумма weight × count по всем позициям. Отрицательный weight_price обнуляется с записью в журнал.
Если порог бесплатной доставки задан (free_delivery_amount больше нуля) и сумма заказа больше или равна ему, стоимость доставки равна 0. Значение поля по умолчанию — 0, то есть при незаполненном пороге бесплатной доставки не будет никогда.
Свой расчёт
Для сложной логики создайте свой класс-обработчик. Контрактов два, и они независимы:
| Интерфейс | Метод | Для чего |
|---|---|---|
DeliveryProviderInterface | getCost() | Расчёт стоимости. Класс указывают в поле class способа доставки |
ShipmentProviderInterface | verifyWebhook(), parseWebhook() | Приём уведомлений службы доставки: проверка подписи и разбор события в ShipmentWebhookEvent |
Уведомления принимает публичный маршрут ядра POST /api/v1/delivery/webhook/{delivery_id} — токен на нём не требуется, подлинность проверяет сам обработчик. Класс только для расчёта реализует первый интерфейс, о приёме уведомлений он ничего не знает.
Пример расчёта:
<?php
namespace MyComponent\Delivery;
use MiniShop3\Controllers\Delivery\DeliveryProviderInterface;
use MiniShop3\Model\msDelivery;
use MiniShop3\Model\msOrder;
class CustomDelivery implements DeliveryProviderInterface
{
public function getCost(msOrder $order, msDelivery $delivery, float $cost): float
{
// Ваша логика расчёта
$cartCost = $order->get('cart_cost');
$weight = $order->get('weight');
if ($cartCost > 10000) {
return 0; // Бесплатно от 10000 руб
}
if ($weight > 5000) {
return 500 + ($weight - 5000) * 0.1; // Наценка за тяжёлый заказ
}
return 300; // Базовая стоимость
}
}Укажите класс в поле class: MyComponent\Delivery\CustomDelivery.
Валидация полей заказа
Для каждого способа доставки задаёте обязательные поля и правила: полный адрес для курьера, телефон для самовывоза.
Визуальный конструктор
Визуальный режим
- Нажмите Добавить поле
- Выберите поле из списка (группы: Заказ, Адрес)
- Добавьте правила валидации для поля
- Для правил с параметрами укажите значение
Правила отображаются тегами, удаляются кликом по крестику.
JSON-режим
Переключатель открывает ручное редактирование JSON:
{
"phone": "required",
"email": "required|email",
"city": "required|min:2",
"street": "required|min:3",
"building": "required"
}В этом режиме правила копируют между доставками, задают regex и переносят JSON между установками.
Свои поля валидации
Кроме стандартных полей заказа и адреса можно добавить произвольные, например чекбокс согласия:
{
"phone": "required",
"email": "required|email",
"agreement": "required|accepted"
}Поля вне стандартного набора (agreement и другие) сохраняются в черновике заказа между шагами order/add и order/submit. При создании заказа они уходят в события msOnBeforeCreateOrder / msOnCreateOrder через параметр customFields.
Чекбоксы
На фронтенде чекбоксы отправляют состояние input.checked ('1' или '0'), а не статический атрибут value. Это обеспечивает корректную работу правила accepted.
Доступные поля для валидации
Поля заказа
| Поле | Описание |
|---|---|
order_comment | Комментарий к заказу |
Поля адреса
| Поле | Описание |
|---|---|
first_name | Имя |
last_name | Фамилия |
phone | Телефон |
email | |
country | Страна |
index | Почтовый индекс |
region | Регион/область |
city | Город |
metro | Станция метро |
street | Улица |
building | Дом/строение |
entrance | Подъезд |
floor | Этаж |
room | Квартира/офис |
comment | Комментарий к адресу |
text_address | Полный адрес текстом |
Правила валидации
Валидацию выполняет встроенный PipeRuleValidator (src/Services/Validation/PipeRuleValidator.php), внешние библиотеки не нужны. Правила комбинируются через |.
Базовые правила
| Правило | Описание |
|---|---|
required | Обязательное поле |
nullable | Поле может быть null |
present | Поле должно присутствовать (даже пустое) |
accepted | Значение должно быть "yes", "on", "1", true |
Типы данных
| Правило | Описание |
|---|---|
email | Валидный email |
url | Валидный URL |
ip | IP адрес (v4 или v6) |
ipv4 | IPv4 адрес |
ipv6 | IPv6 адрес |
numeric | Числовое значение |
integer | Целое число |
boolean | Булево значение |
array | Массив |
json | Валидный JSON |
Строковые правила
| Правило | Описание |
|---|---|
alpha | Только буквы |
alpha_num | Буквы и цифры |
alpha_dash | Буквы, цифры, дефис, подчёркивание |
alpha_spaces | Буквы и пробелы |
uppercase | Только заглавные буквы |
lowercase | Только строчные буквы |
Правила с параметрами
| Правило | Описание | Синтаксис |
|---|---|---|
min | Минимальная длина строки или значение числа | min:3 |
max | Максимальная длина строки или значение числа | max:100 |
between | Значение в диапазоне | between:1,10 |
digits | Точное количество цифр | digits:6 |
digits_between | Количество цифр в диапазоне | digits_between:4,8 |
in | Значение из списка | in:pickup,courier,post |
not_in | Значение НЕ из списка | not_in:test,demo |
same | Совпадает с другим полем | same:email_confirm |
different | Отличается от другого поля | different:old_password |
regex | Соответствует регулярному выражению | regex:/^[0-9]{6}$/ |
extension | Расширение файла | extension:jpg,png |
mimes | MIME-тип файла | mimes:jpg,png |
Правила для дат
| Правило | Описание | Синтаксис |
|---|---|---|
date | Валидная дата в формате | date:Y-m-d |
after | Дата после указанной | after:2024-01-01 |
before | Дата до указанной | before:2025-12-31 |
Условные правила
| Правило | Описание | Синтаксис |
|---|---|---|
required_if | Обязательно, если другое поле = значению | required_if:delivery,courier |
required_unless | Обязательно, если другое поле ≠ значению | required_unless:delivery,pickup |
required_with | Обязательно, если указано другое поле | required_with:phone |
required_without | Обязательно, если НЕ указано другое поле | required_without:email |
required_with_all | Обязательно, если указаны ВСЕ поля | required_with_all:city,street |
required_without_all | Обязательно, если НЕ указано НИ ОДНО поле | required_without_all:phone,email |
У части правил нет своего текста ошибки
Проверка работает, но текста в PipeRuleValidator::DEFAULT_MESSAGES нет у after, before, digits_between и всех шести required_*. При провале покупатель увидит общее «The :attribute has unsupported validation rule», а задать свой текст нельзя (см. Сообщения об ошибках).
Примеры конфигураций
Одинаковые строки правил схлопываются
OrderFieldManager::validate() склеивает наборы через array_unique(array_merge(...)), а array_unique работает по значениям. Если двум полям задана одна и та же строка правил, в наборе останется только первое — у остальных форматные проверки (min, email, regex) не применятся. Обязательность при этом всё равно проверяется на сабмите другим путём.
В примерах ниже такие повторы есть; чтобы правила работали для каждого поля, строки должны различаться — например required|min:2 и required|min:3. Дефект заведён как modx-pro/MiniShop3#949.
Курьерская доставка
Требуется полный адрес:
{
"first_name": "required|min:2",
"last_name": "required|min:2",
"phone": "required|regex:/^\\+?[0-9]{10,15}$/",
"email": "required|email",
"city": "required|min:2",
"street": "required|min:3",
"building": "required",
"room": "required_if:building_type,apartment"
}Самовывоз
Минимум данных для связи:
{
"first_name": "required|min:2",
"phone": "required"
}Почтовая доставка
Требуется индекс и полный адрес:
{
"first_name": "required",
"last_name": "required",
"phone": "required",
"index": "required|digits:6",
"region": "required",
"city": "required",
"street": "required",
"building": "required"
}Доставка в постамат
Только контактные данные:
{
"first_name": "required",
"phone": "required|regex:/^\\+?[0-9]{10,15}$/",
"email": "required|email"
}Поля в конструкторе
Список полей в конструкторе правил не статичен: к встроенному набору добавляются поля msOrder и msOrderAddress из «Полей форм (админка)» и из «Своих полей». Технические колонки вроде id и служебных дат из списка исключаются.
То есть своё поле заказа, заведённое в утилитах, появляется в конструкторе само — отдельно прописывать его не нужно.
Сообщения об ошибках
Сообщения по умолчанию английские: язык интерфейса они не читают. Для полей заказа действуют четыре текста, зашитые в OrderFieldManager::validate() — для required, numeric, min и email; остальные приходят из PipeRuleValidator::DEFAULT_MESSAGES.
Своих сообщений задать нельзя
setValidationMessages() и setValidationRules() затираются в начале каждого вызова validate(), а properties доставки валидация не читает вовсе: источником правил служит только колонка validation_rules. Рабочего способа подменить тексты в текущем коде нет — дефект заведён как modx-pro/MiniShop3#950.
Manager API
Эндпоинты админки. Вся группа закрыта правом mssetting_save.
| Метод | Роут | Описание |
|---|---|---|
GET | /api/mgr/deliveries | Список. Фильтры filter_name и filter_active |
POST | /api/mgr/deliveries | Создать |
GET / PUT / DELETE | /api/mgr/deliveries/{id} | Чтение, правка, удаление |
DELETE | /api/mgr/deliveries/bulk | Массовое удаление |
POST | /api/mgr/deliveries/sort | Порядок после перетаскивания |
PUT | /api/mgr/deliveries/positions | Проставить позиции списком |
POST / DELETE | /api/mgr/deliveries/{id}/payments | Связать и отвязать способ оплаты |
GET | /api/mgr/deliveries-active | Активные доставки для выпадающих списков формы заказа. Отдаёт белый список полей, без properties и прочих секретов |
У оплат набор зеркальный: /api/mgr/payments, /sort, /positions, /bulk, /{id}/deliveries и GET /api/mgr/payments-active (принимает delivery_id, чтобы оставить только связанные).
Web API
Оба GET-запроса идут с токеном магазина: заголовок Bearer/MS3Token или кука ms3_token. Query-параметром токен не принимается — TokenMiddleware вырезает его из строки запроса. Передавать токен вручную при этом не обязательно: на маршруте без токена сервер сам выпустит анонимный, так что запрос не упадёт.
Правила возвращаются только для активной доставки
Выборка правил идёт с условием active = 1. Для выключенного способа доставки оба эндпоинта вернут пустой набор, и это неотличимо от «у доставки нет своих правил».
Получение правил валидации
GET /api/v1/order/delivery/validation-rules?delivery_id=1Ответ:
{
"success": true,
"data": {
"validation_rules": {
"phone": "required",
"city": "required|min:2",
"street": "required"
}
}
}Получение обязательных полей
GET /api/v1/order/delivery/required-fields?delivery_id=1Ответ (карта поле → правила, только с required):
{
"success": true,
"data": {
"requires": {
"phone": "required",
"city": "required",
"street": "required"
}
}
}Эти два запроса обновляют форму заказа при смене способа доставки.
