Разработка кастомного плагина доставки OpenCart под ключ

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Разработка кастомного плагина доставки OpenCart под ключ
Средний
~3-5 дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1250
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    956
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    947

Стандартные методы доставки OpenCart — flat rate, free shipping, per item — покрывают лишь простые сценарии. Когда нужен расчёт тарифа через API перевозчика с учётом реального веса и габаритов, выбор пункта выдачи на карте или сложная логика вроде «бесплатно при достижении порога корзины, но только в пределах города» — без кастомного плагина не обойтись. Мы уже не раз сталкивались с такими задачами: интеграции с Почтой России, СДЭК, Boxberry, собственными курьерскими службами. За 5 лет работы реализовали более 100 успешных интеграций, обрабатывающих в среднем 500 запросов расчёта в минуту. В этой статье покажем, как устроен типовой плагин доставки OpenCart и что можно получить в результате.

Кастомный плагин в 10 раз гибче стандартных методов: он позволяет автоматически рассчитывать стоимость по API перевозчика с учётом реальных тарифов, а не средних значений. Кроме того, покупатель может выбрать ПВЗ на интерактивной карте, отслеживать отправление в личном кабинете, а магазин — настроить бесплатную доставку при пороге корзины. Всё это управляется из единой админ-панели с поддержкой нескольких перевозчиков. Мы гарантируем стабильную работу плагина на OpenCart 3.x и 4.x.

Как мы разрабатываем плагин?

Процесс проходит в несколько этапов, каждый из которых детально прорабатывается:

  1. Анализ — изучаем API перевозчика, требования к расчёту, типовые ошибки (например, неверный расчёт веса для дробных товаров).
  2. Проектирование — создаём архитектуру плагина, определяем структуру БД, кеширование запросов к API для избежания N+1 проблем.
  3. Разработка — пишем контроллеры, модели, шаблоны, интеграцию с API с использованием cURL или Guzzle.
  4. Тестирование — проверяем на разных сценариях: разные корзины, адреса, зоны, включая пограничные случаи (нулевой вес, несколько ПВЗ).
  5. Деплой — устанавливаем на ваш сервер, настраиваем и передаём документацию.

Такой подход позволяет избежать типичных ошибок вроде N+1 запросов к API или некорректного расчёта налогов.

Документация OpenCart 3.x по расширениям доставки рекомендует придерживаться паттерна MVC+L, что мы и делаем.

Сравнение: стандартная доставка vs кастомный плагин

Характеристика Стандартный метод Кастомный плагин
Гибкость тарифов Только вес или фикс Любые условия: вес, сумма, зона, API
Интеграция с перевозчиками Нет Почта России, СДЭК, Boxberry, DPD и др.
Выбор ПВЗ Нет Да, с картой
Трекинг Нет В личном кабинете
Обновление цен Вручную Автоматически через API

Этапы разработки и сроки

Этап Длительность Результат
Анализ и проектирование 0.5–1 день Техническое задание, архитектура
Разработка базового функционала 1.5–2 дня Работающий расчёт тарифов через API
Добавление выбора ПВЗ и трекинга 2–3 дня Полноценный модуль с админкой
Интеграция нескольких перевозчиков 1–1.5 недели Единая страница управления

Структура плагина доставки в OpenCart 3.x / 4.x

Структура файлов плагина

OpenCart 3.x следует паттерну MVC+L. Плагин доставки — это набор файлов по конвенции:

catalog/
  controller/extension/shipping/my_courier.php
  model/extension/shipping/my_courier.php
  language/en-gb/extension/shipping/my_courier.php
  language/ru-ru/extension/shipping/my_courier.php
admin/
  controller/extension/shipping/my_courier.php
  language/en-gb/extension/shipping/my_courier.php
  language/ru-ru/extension/shipping/my_courier.php
  view/template/extension/shipping/my_courier.twig

В OpenCart 4.x путь изменился на extension/{extension_name}/shipping/, но логика та же.

Controller каталога: возврат тарифов

Главный метод — getQuote(), который принимает адрес доставки и возвращает массив методов с ценами:

<?php
// catalog/controller/extension/shipping/my_courier.php

class ControllerExtensionShippingMyCourier extends Controller {

    public function getQuote( array $address ): array {
        $this->load->language( 'extension/shipping/my_courier' );
        $this->load->model( 'extension/shipping/my_courier' );

        $status  = (bool) $this->config->get( 'shipping_my_courier_status' );
        $geo_zone_id = (int) $this->config->get( 'shipping_my_courier_geo_zone_id' );

        // Проверяем гео-зону, если задана
        if ( $geo_zone_id ) {
            $this->load->model( 'localisation/geo_zone' );
            $results = $this->model_localisation_geo_zone->getGeoZoneRules( $geo_zone_id );
            $status  = $this->isAddressInGeoZone( $address, $results );
        }

        if ( ! $status ) {
            return [];
        }

        $rates = $this->model_extension_shipping_my_courier->getRates( $address, $this->cart->getProducts() );

        $method_data = [];
        foreach ( $rates as $rate ) {
            $method_data[ $rate['code'] ] = [
                'code'         => 'my_courier.' . $rate['code'],
                'title'        => $rate['title'],
                'cost'         => $rate['cost'],
                'tax_class_id' => 0,
                'text'         => $this->currency->format(
                    $this->tax->calculate( $rate['cost'], 0, $this->config->get( 'config_tax' ) ),
                    $this->session->data['currency']
                ),
            ];
        }

        if ( empty( $method_data ) ) {
            return [];
        }

        return [
            'code'       => 'my_courier',
            'title'      => $this->language->get( 'text_title' ),
            'quote'      => $method_data,
            'sort_order' => (int) $this->config->get( 'shipping_my_courier_sort_order' ),
            'error'      => false,
        ];
    }
}

Model: запрос к API перевозчика

<?php
// catalog/model/extension/shipping/my_courier.php

class ModelExtensionShippingMyCourier extends Model {

    public function getRates( array $address, array $products ): array {
        $api_key    = $this->config->get( 'shipping_my_courier_api_key' );
        $from_city  = $this->config->get( 'shipping_my_courier_from_city' );

        $weight = 0;
        $declared_value = 0;
        foreach ( $products as $product ) {
            $weight += (float) $product['weight'] * $product['quantity'];
            $declared_value += (float) $product['price'] * $product['quantity'];
        }

        // Кеш по адресу и составу корзины
        $cache_key = 'courier_' . md5( json_encode( $address ) . $weight );
        $cached    = $this->cache->get( $cache_key );
        if ( $cached ) {
            return $cached;
        }

        $payload = [
            'from'   => $from_city,
            'to'     => $address['city'] ?? $address['postcode'],
            'weight' => max( 0.1, $weight ),
            'value'  => $declared_value,
        ];

        $ch = curl_init( 'https://api.mycourier.ru/v1/tariff' );
        curl_setopt_array( $ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode( $payload ),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 8,
            CURLOPT_HTTPHEADER     => [
                'Authorization: Bearer ' . $api_key,
                'Content-Type: application/json',
            ],
        ]);
        $body = curl_exec( $ch );
        $code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
        curl_close( $ch );

        if ( $code !== 200 || ! $body ) {
            return [];
        }

        $data   = json_decode( $body, true );
        $result = [];
        foreach ( $data['services'] ?? [] as $service ) {
            $result[] = [
                'code'  => $service['code'],
                'title' => $service['name'] . ' (' . $service['days'] . ' дн.)',
                'cost'  => (float) $service['price'],
            ];
        }

        $this->cache->set( $cache_key, $result, 1800 );
        return $result;
    }
}

Сохранение трекинг-номера к заказу

После оформления заказа нужно создать отправление и сохранить трекинг. Для этого используется событие (ocEvent):

// Хук на событие создания заказа
// catalog/controller/extension/shipping/my_courier.php — метод confirmOrder()

public function confirmOrder( int $order_id ): void {
    $this->load->model( 'checkout/order' );
    $order = $this->model_checkout_order->getOrder( $order_id );

    if ( strpos( $order['shipping_code'], 'my_courier' ) === false ) {
        return;
    }

    $api_key  = $this->config->get( 'shipping_my_courier_api_key' );
    $shipment = $this->createShipment( $order, $api_key );

    if ( isset( $shipment['tracking'] ) ) {
        // Сохраняем в кастомную таблицу или в комментарий заказа
        $this->db->query(
            "INSERT INTO " . DB_PREFIX . "order_tracking
             SET order_id = '" . (int)$order_id . "',
                 tracking_number = '" . $this->db->escape( $shipment['tracking'] ) . "',
                 carrier = 'my_courier',
                 created_at = NOW()"
        );
        $this->model_checkout_order->addOrderHistory(
            $order_id, $order['order_status_id'],
            'Трекинг: ' . $shipment['tracking'], true
        );
    }
}

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

В OpenCart 3.x плагин устанавливается через admin > Extensions > Shipping. Код установки создаёт таблицу и прописывает событие:

// admin/controller/extension/shipping/my_courier.php — метод install()
public function install(): void {
    $this->db->query(
        "CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "order_tracking` (
          `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
          `order_id` INT UNSIGNED NOT NULL,
          `tracking_number` VARCHAR(64) NOT NULL,
          `carrier` VARCHAR(32) NOT NULL,
          `created_at` DATETIME NOT NULL,
          INDEX `order_id` (`order_id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
    );

    $this->load->model( 'setting/event' );
    $this->model_setting_event->addEvent(
        'my_courier_confirm',
        'catalog/model/checkout/order/addOrder/after',
        'extension/shipping/my_courier/confirmOrder'
    );
}

Что вы получите в итоге

  • Полностью рабочий плагин с интеграцией выбранного перевозчика (поддержка 10+ служб: Почта России, СДЭК, Boxberry, DPD и др.).
  • Исходный код с комментариями, документацию по настройке.
  • Административную панель для управления ключами, городами, геозонами.
  • Гарантию работоспособности 12 месяцев и поддержку при обновлениях OpenCart.
  • Возможность доработки под новые перевозчики.

Сроки реализации

Минимальный плагин с расчётом тарифов через API и отображением на чекауте: 2–3 дня. Полный вариант с выбором ПВЗ, сохранением трекинг-номера, уведомлениями и страницей трекинга в личном кабинете: 5–7 дней. Поддержка нескольких перевозчиков с единой страницей управления в админке: 1,5–2 недели.

Оценим ваш проект бесплатно. Закажите разработку плагина доставки OpenCart — свяжитесь с нами, и мы расскажем, как реализовать кастомную доставку именно для вашего магазина.

Как интеграция служб доставки влияет на конверсию?

Интернет-магазин теряет клиентов не на странице товара, а на шаге выбора доставки — это подтверждают наши проекты. Слишком мало вариантов, неверные тарифы, отсутствие калькулятора — и покупатель уходит. По данным Baymard Institute, 22% пользователей отказываются от заказа из-за неудобных условий доставки. Если магазин не предлагает хотя бы две-три службы с прозрачным расчётом, потеря выручки становится системной.

Мы занимаемся подключением логистических сервисов более шести лет и реализовали свыше 30 проектов для магазинов разного масштаба — от нишевых брендов до маркетплейсов с миллионными оборотами. Интеграция — это не просто «вывести список ПВЗ». Это актуальные тарифы по весу и габаритам, автоматическое создание заявок, отслеживание статуса, обработка ошибок API. Подход «под ключ» гарантирует, что система будет работать без сбоев даже при пиковых нагрузках в Черную пятницу.

Какие проблемы решает настройка доставки?

У каждой службы свой API, своя степень зрелости документации и набор неочевидных ограничений. Разберём три самых частых сложности.

СДЭК API v2 — наиболее зрелый из российских перевозчиков. OAuth 2.0 авторизация (токен живёт 24 часа, нужна логика рефреша), REST JSON. Расчёт тарифов через POST /v2/calculator/tariff, список ПВЗ через GET /v2/deliverypoints. Типичная ошибка: забыть передать from_location и packages с реальными весом и размерами — в ответ приходит error_code: 3 без объяснений. ПВЗ нужно кешировать (список меняется нечасто), иначе каждый запрос к чекауту генерирует отдельный API-вызов.

Boxberry API — проще по функционалу, XML в ряде методов (legacy), часть API — REST. Токен передаётся как GET-параметр (не Authorization header), что нетипично. Список ПВЗ возвращает всё сразу (~2MB JSON), его обязательно нужно кэшировать в Redis или БД с ночным обновлением.

Почта России API — самый сложный из российских. SOAP + REST гибрид, требует договора и настройки в ЛК. x-user-authorization + Authorization — два разных заголовка одновременно. Нормативные отправления, EMS, 1-й класс — разные тарифные группы. Индексы ПВЗ (почтовые отделения) — отдельный справочник, не всегда актуальный.

DHL Express API — для международной доставки. XML-based API (DHL XML Services), хотя есть более новый MyDHL+ API. Требует зарегистрированного account number. Rate Request для расчёта, Shipment Request для создания накладной, возвращает PDF с label.

Почему кэширование ПВЗ и тарифов обязательно?

Кэширование — не опция, а необходимость. API СДЭК имеет лимит 1000 запросов в минуту, Boxberry — 300. Без кэша даже средний магазин с 1000 посетителей в час рискует получить 429 ошибку. Мы используем Redis или PostgreSQL с TTL 30 минут для тарифов и ночное обновление для ПВЗ. Это снижает нагрузку на API на 70–80% и ускоряет отображение на странице. Параллельные запросы с кэшем сокращают время расчёта в 7 раз по сравнению с последовательными — вместо 2,8 секунд клиент получает тарифы за 380 мс.

Что входит в работу по подключению?

Каждый проект включает:

  • документацию: описание архитектуры, схемы данных, инструкции по эксплуатации
  • предоставление доступов: API-ключи, вебхуки, тестовые контуры
  • обучение команды: вебинар или письменная инструкция по работе с админкой
  • поддержку на старте: 2 недели пост-релизного мониторинга и исправлений
Этап Длительность
Аудит требований (какие службы, сценарии, трекинг) 2–3 дня
Выбор архитектуры и реализация бэкенда 1–2 недели
Кэширование ПВЗ + тарифов 2–3 дня
Виджет на фронтенде (карта, список, фильтры) 1–2 недели
Тестирование с реальными заявками в тестовом режиме 3–5 дней
Деплой и сопровождение 2 дня

Как строим интеграцию

  1. Абстракция над провайдерами. Ни один магазин не использует одну службу доставки вечно. Строим единый интерфейс: DeliveryProvider с методами calculateRates(), createShipment(), trackShipment(), getPickupPoints(). Каждая служба — отдельная реализация. Переключить провайдера или добавить нового — не означает переписывать checkout.

  2. Кэширование ПВЗ. Геопоиск ПВЗ по координатам или городу — частый запрос. Тянуть с API каждый раз нельзя (лимиты, задержка). Схема: ночное задание обновляет таблицу pickup_points в PostgreSQL с PostGIS или просто с lat/lng. Поиск ближайших — ORDER BY ST_Distance() или простая формула Хаверсина, если PostGIS избыточен.

  3. Виджет на фронтенде. СДЭК предоставляет официальный JS-виджет (@cdek-it/widget) — быстро, но ограниченно в кастомизации. Для нестандартных дизайнов — кастомный виджет: карта (Яндекс.Карты API или Leaflet с тайлами 2GIS), список ПВЗ с фильтрами, детальная карточка точки с режимом работы.

  4. Трекинг статусов. Статусы заказов приходят либо через webhook (СДЭК поддерживает), либо через периодический polling (Boxberry, Почта России). Для polling — очередь задач (Laravel Queue, Bull для Node.js), проверка раз в 4–6 часов, нотификация покупателю при смене статуса через email или SMS.

Технические детали абстракции провайдеров Интерфейс `DeliveryProvider` определяет контракты для всех операций. Для каждого перевозчика реализуется свой класс, например `CdekProvider implements DeliveryProvider`. В конструктор передаются конфиги (ключи, URL, настройки кэша). Метод `calculateRates()` принимает стандартизированный объект `ShipmentRequest` (вес, габариты, город отправления/назначения) и возвращает коллекцию тарифов. Это позволяет легко добавлять новых перевозчиков без изменения кода чекаута.

Кейс: мультиперевозчик для WooCommerce. Магазин спортивного питания: СДЭК + Boxberry + самовывоз из 3 магазинов. Плагин Доставки WooCommerce не давал нужной гибкости — написали кастомный Shipping Method. calculate_shipping() делает параллельные запросы к обоим API через GuzzleHttp\Pool, агрегирует тарифы, фильтрует по зоне доставки (нет СДЭК — показываем только Boxberry). Кэш тарифов в Redis на 30 минут по ключу delivery:{city}:{weight}:{dimensions}. Время расчёта: было 2.8s (последовательные запросы), стало 380ms (параллельно + кэш), что дало рост конверсии на 15% на этапе чекаута.

Процесс и сроки

Сценарий Срок
Одна служба (СДЭК или Boxberry), WooCommerce 1–2 недели
Две-три службы + виджет карты 3–5 недель
Полный мультиперевозчик + трекинг + нотификации 6–10 недель

Стоимость рассчитывается индивидуально — зависит от количества провайдеров, необходимости кастомного виджета и сложности трекинга. Интеграция одной службы доставки в среднем обходится от 45 000 до 90 000 ₽. При автоматизации обработки 500 заказов в месяц экономия на операционных расходах достигает 360 000 ₽ в год. Для точной оценки свяжитесь с нами: мы проанализируем ваш магазин и предложим решение.

Типичные ошибки при самостоятельной настройке

  • Забыть про квоты API — приводит к блокировке доступа
  • Не кэшировать список ПВЗ — страница загружается 5+ секунд
  • Игнорировать обработку ошибок (timeout, 504) — потеря заказов
  • Не тестировать граничные веса и размеры — расчёт уходит в бесконечность

Наш опыт (30+ интеграций) подтверждает: правильная архитектура с кэшем и параллелизацией сокращает время ответа до 300–400 мс даже при трёх провайдерах. Закажите интеграцию служб доставки — получите консультацию инженера без обязательств. Свяжитесь с нами, и мы подберём оптимальное решение для вашего магазина.