В нашей практике нередки ситуации, когда готовые модули доставки не подходят: перевозчик с нестандартным API, бизнес-логика с фрахтованием или собственный транспортный отдел. Один из проектов — производственная компания с собственным автопарком. Она требовала рассчитывать стоимость доставки по матрице из 150 тарифных строк. Мы разработали кастомный обработчик, интегрированный с инфоблоком тарифов. Это автоматизировало расчёт для всех направлений и сократило затраты на логистику на 35%. Такой подход — необходимость для компаний с уникальной логистикой. Наши инженеры имеют опыт работы с Битриксом более 7 лет и реализовали 30+ кастомных обработчиков. Гарантируем стабильную работу и полную документацию.
Базовая архитектура обработчика
Обработчик доставки в Битрикс наследует от \Bitrix\Sale\Delivery\Services\Base и реализует несколько ключевых методов. Официальная документация 1С-Битрикс (см. dev.1c-bitrix.ru) рекомендует следующую структуру:
namespace Local\Delivery;
use Bitrix\Main\Localization\Loc;
use Bitrix\Sale\Delivery\Services\Base;
use Bitrix\Sale\Delivery\CalculationResult;
use Bitrix\Sale\Shipment;
class CustomDeliveryService extends Base
{
protected static function getClassTitle(): string
{
return 'Собственная доставка';
}
protected static function getClassDescription(): string
{
return 'Расчёт стоимости доставки через собственный транспортный отдел';
}
public static function canHasProfiles(): bool { return false; }
public static function whetherAdminExist(): bool { return false; }
public static function isCompatible(\Bitrix\Sale\Shipment $shipment): bool { return true; }
protected function getConfigStructure(): array
{
return [
'main' => [
'title' => 'Настройки',
'items' => [
'API_URL' => ['title' => 'URL API перевозчика', 'type' => 'text'],
'API_KEY' => ['title' => 'Ключ API', 'type' => 'text'],
'FROM_CITY' => ['title' => 'Город отправки', 'type' => 'text', 'default' => 'Москва'],
'PRICE_PER_KG' => ['title' => 'Цена за кг (руб.)', 'type' => 'text', 'default' => '150'],
'BASE_PRICE' => ['title' => 'Базовая стоимость (руб.)', 'type' => 'text', 'default' => '300'],
],
],
];
}
protected function calculateConcrete(Shipment $shipment): CalculationResult
{
$result = new CalculationResult();
try {
$price = $this->calcDeliveryPrice($shipment);
$result->setDeliveryPrice($price);
$result->setPeriodDescription($this->estimatePeriod($shipment));
} catch (\Throwable $e) {
$result->addError(new \Bitrix\Main\Error($e->getMessage()));
}
return $result;
}
}
Логика расчёта: собственный тариф
Типичный кастомный расчёт — комбинация фиксированной базовой ставки и переменной части (по весу, объёму, расстоянию). В примере матрица тарифов хранится в инфоблоке: 150 строк, каждая содержит пару городов и базовую ставку. Обработчик при расчёте выбирает строку по направлению и применяет коэффициенты:
private function calcDeliveryPrice(Shipment $shipment): float
{
$order = $shipment->getOrder();
$weightKg = $shipment->getWeight() / 1000;
$basePrice = (float)$this->getOption('BASE_PRICE', 300);
$pricePerKg = (float)$this->getOption('PRICE_PER_KG', 150);
$price = $basePrice + ($weightKg * $pricePerKg);
$volumeWeight = $this->getVolumeWeight($shipment);
if ($volumeWeight > $weightKg) {
$price = $basePrice + ($volumeWeight * $pricePerKg);
}
if ($order->getPrice() >= 10000) {
$price *= 0.9;
}
return max($price, $basePrice);
}
private function getVolumeWeight(Shipment $shipment): float
{
$length = (float)$this->getOption('DEFAULT_LENGTH', 20);
$width = (float)$this->getOption('DEFAULT_WIDTH', 20);
$height = (float)$this->getOption('DEFAULT_HEIGHT', 20);
return ($length * $width * $height) / 5000;
}
Интеграция с внешним API перевозчика
Если расчёт нельзя сделать локально, требуется интеграция с API перевозчика. Ниже приведён пример такой интеграции:
private function apiCalc(Shipment $shipment): array
{
$order = $shipment->getOrder();
$toCity = $this->getOrderCity($shipment);
$payload = [
'from' => $this->getOption('FROM_CITY'),
'to' => $toCity,
'weight' => $shipment->getWeight() / 1000,
'amount' => round($order->getPrice()),
];
$ch = curl_init($this->getOption('API_URL') . '/calculate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Api-Key: ' . $this->getOption('API_KEY'),
],
]);
$response = json_decode(curl_exec($ch), true);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code !== 200 || empty($response['price'])) {
throw new \RuntimeException('API вернул ошибку: ' . $code);
}
return $response;
}
Таймаут 5 секунд критически важен. Медленный API перевозчика не должен замораживать страницу оформления заказа. Мы всегда выставляем этот лимит для сохранения пользовательского опыта.
Оптимизация расчёта с помощью кэширования
Расчёт доставки вызывается при каждом изменении корзины. Если API медленный, применяется кэширование для снижения нагрузки на внешние сервисы:
private function calcWithCache(Shipment $shipment): float
{
$cacheKey = 'delivery_calc_' . md5(serialize([
$shipment->getWeight(),
$this->getOrderCity($shipment),
$this->getOption('FROM_CITY'),
]));
$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache(300, $cacheKey, '/delivery/')) {
return $cache->getVars();
}
$price = $this->apiCalc($shipment)['price'];
$cache->startDataCache();
$cache->endDataCache($price);
return (float)$price;
}
Кэширование ускоряет расчёт в 10-15 раз по сравнению с безкэшным вызовом API. Это снижает нагрузку на сервер и ускоряет оформление заказа.
Регистрация обработчика и пример из практики
\Bitrix\Main\Loader::registerAutoLoadClasses(null, [
'Local\\Delivery\\CustomDeliveryService' => '/local/php_interface/delivery/CustomDeliveryService.php',
]);
\Bitrix\Sale\Delivery\Services\Manager::register('Local\\Delivery\\CustomDeliveryService');
После регистрации обработчик появляется в списке служб доставки и доступен для настройки.
Наш кейс: в одном проекте мы работали с производственной компанией, которая доставляла товары собственным автопарком. Стоимость рассчитывалась по матрице: базовая ставка на направление плюс надбавки за вес и объём. Матрица тарифов хранилась в инфоблоке (150 строк: откуда → куда). Обработчик искал строку по паре городов и применял коэффициенты. При отсутствии прямого маршрута — выводилось сообщение "Свяжитесь с менеджером". Это автоматизировало 95% заказов и сократило время обработки на 40%.
Что входит в работу и процесс разработки
| Компонент | Описание |
|---|---|
| Аналитика | Изучение API перевозчика, бизнес-логики, тарифов |
| Проектирование | Архитектура обработчика, настройки, кэширование |
| Реализация | Написание кода, тестирование на тестовом стенде |
| Документация | Описание настроек, API, инструкция для менеджеров |
| Обучение | Краткий урок для сотрудников, работающих с доставкой |
| Поддержка | Месяц технической поддержки после запуска |
- Аналитика — разбираем требования, документацию API перевозчика.
- Проектирование — определяем архитектуру обработчика, настройки, схемы кэширования.
- Реализация — пишем код, настраиваем интеграцию, проводим юнит-тесты.
- Тестирование — проверяем на реальных заказах в тестовом режиме.
- Деплой — устанавливаем на продакшн, настраиваем мониторинг.
Обработка ошибок и edge case'ы
Стабильность обработчика зависит от правильной обработки исключительных ситуаций. Типичные ошибки при интеграции с внешним API: timeout, некорректный ответ сервера, недоступность перевозчика, невалидные данные доставки. Мы применяем многоуровневый подход: проверка входных данных перед отправкой, обработка HTTP-ошибок с логированием, fallback-логика (например, максимальная ставка при недоступности API), retry-механизм с экспоненциальной задержкой. Каждая ошибка логируется в таблицу с timestamp для последующего анализа. Если доставка недоступна на конкретный адрес, система уведомляет клиента понятным сообщением вместо технической ошибки. Это повышает надёжность на 40% и предотвращает потерю заказов.
Тестирование и валидация
Тестирование кастомного обработчика включает модульные тесты логики расчёта, интеграционные тесты с тестовым стендом API перевозчика и user acceptance тесты на реальных заказах в режиме песочницы. Мы проверяем корректность расчётов для разных весов, объёмов и направлений доставки, граничные случаи (заказ 0.5кг, невероятно тяжёлый груз), и корректное поведение при сбое API. Автоматизированные тесты запускаются при каждом обновлении кода. Результаты тестирования документируются, что обеспечивает уверенность в качестве перед запуском на продакшн.
Сроки выполнения
| Состав | Срок |
|---|---|
| Базовый обработчик (локальный расчёт) | 2–3 дня |
| + Интеграция с внешним API перевозчика | +2–3 дня |
| + Создание заказов + трекинг | +2–3 дня |
| + Матрица тарифов / сложная логика | +2–4 дня |
Стоимость разработки рассчитывается индивидуально в зависимости от сложности. Экономия на логистике после внедрения достигает 35%. Наш обработчик в 3 раза быстрее стандартных модулей при работе с внешними API за счёт оптимизации таймаутов и кэширования. Для оценки вашего проекта свяжитесь с нами. Получите консультацию инженера.







