Интеграция службы доставки DPD на сайт

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Интеграция службы доставки DPD на сайт
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Интеграция службы доставки DPD на сайт

DPD — международный логистический оператор с разветвлённой сетью в России и странах СНГ. API предоставляется через SOAP-сервисы, что несколько архаично по меркам современных REST-интеграций, но вполне работоспособно. Для PHP есть официальный SDK, который скрывает сложность SOAP под удобным фасадом.

Подключение и инициализация

// composer require dpd-ru/dpd-php-api-client
use DPD\DPDClient;

$client = new DPDClient(
    username: config('services.dpd.username'),
    password: config('services.dpd.password'),
    clientNumber: config('services.dpd.client_number'),
    test: app()->environment('production') === false
);

Тестовая среда (test: true) работает на https://test.dpd.ru. Учётные данные для теста запрашиваются у менеджера DPD отдельно от боевых.

Расчёт стоимости

DPD предоставляет метод getDPDOrderCost2 для расметки тарифов:

public function calculateDelivery(
    string $fromCityId,   // код города DPD (не ISO)
    string $toCityId,
    float  $weightKg,
    array  $dimensions,
    float  $declaredValue = 0
): array {
    $response = $this->client->getDPDOrderCost2([
        'pickup'  => [
            'CityID'   => $fromCityId,
            'CountryCode' => 'RU',
        ],
        'delivery' => [
            'CityID'   => $toCityId,
            'CountryCode' => 'RU',
        ],
        'selfPickup'   => false,
        'selfDelivery' => false,
        'weight'       => $weightKg,
        'volume'       => ($dimensions['length'] * $dimensions['width'] * $dimensions['height']) / 1000000, // м³
        'serviceCode'  => 'ALL', // получить все доступные сервисы
        'declaredValue'=> $declaredValue,
    ]);

    return collect($response->return ?? [])
        ->filter(fn($r) => $r->result === 'OK')
        ->map(fn($r) => [
            'service_code' => $r->serviceCode,
            'service_name' => $r->serviceName,
            'cost'         => (float)$r->cost,
            'min_days'     => (int)$r->days,
            'max_days'     => (int)$r->days + 1,
        ])
        ->values()
        ->toArray();
}

Коды сервисов: BZP — бизнес-посылка (дверь-дверь), PCL — посылка до терминала, MAX — максималка (крупные грузы), EXPRESS — экспресс-доставка.

Поиск городов

DPD использует собственные ID городов. Поиск по названию:

public function findCity(string $cityName, string $countryCode = 'RU'): array
{
    $response = $this->client->getCitiesCashPay([
        'countryCode' => $countryCode,
        'regionCode'  => null,
        'cityName'    => $cityName,
    ]);

    return collect($response->return ?? [])
        ->map(fn($c) => [
            'id'          => $c->cityId,
            'name'        => $c->cityName,
            'region'      => $c->regionName,
            'abbreviation'=> $c->regionCode,
        ])
        ->toArray();
}

Рекомендуется кешировать справочник городов локально (синхронизировать раз в неделю), так как запросы к getCitiesCashPay работают медленно.

Пункты выдачи (терминалы)

public function getTerminals(?string $cityId = null): array
{
    $params = [];
    if ($cityId) {
        $params['cityId'] = $cityId;
    }

    $response = $this->client->getTerminalsSelfDelivery2($params);

    return collect($response->return ?? [])
        ->map(fn($t) => [
            'id'         => $t->terminalCode,
            'name'       => $t->terminalName,
            'address'    => $t->address,
            'city'       => $t->city,
            'lat'        => (float)($t->geoCoordinates->latitude ?? 0),
            'lng'        => (float)($t->geoCoordinates->longitude ?? 0),
            'work_time'  => $this->formatWorkTime($t->schedule ?? []),
            'phone'      => $t->phone ?? null,
            'email'      => $t->email ?? null,
            'cash_allowed' => (bool)($t->payCash ?? false),
            'card_allowed' => (bool)($t->payCard ?? false),
        ])
        ->toArray();
}

Создание заказа

public function createOrder(Order $order): array
{
    $response = $this->client->createOrder([
        'header' => [
            'datePickup'      => now()->addDay()->format('Y-m-d') . 'T10:00:00',
            'senderAddress'   => [
                'name'        => config('services.dpd.sender_name'),
                'countryCode' => 'RU',
                'zipCode'     => config('services.dpd.sender_zip'),
                'city'        => config('services.dpd.sender_city'),
                'street'      => config('services.dpd.sender_street'),
                'house'       => config('services.dpd.sender_house'),
                'contactFio'  => config('services.dpd.contact_name'),
                'contactPhone'=> config('services.dpd.contact_phone'),
            ],
            'pickupTimePeriod'=> '10:00-18:00',
            'senderPhone'     => config('services.dpd.contact_phone'),
        ],
        'order' => [[
            'orderNum'        => (string)$order->id,
            'serviceCode'     => $order->dpd_service_code,
            'serviceVariant'  => 'ДД', // ДД=дверь-дверь, ДТ=дверь-терминал
            'cargoNumPack'    => 1,
            'cargoWeight'     => $order->total_weight_kg,
            'cargoVolume'     => $this->calculateVolume($order),
            'cargoRegistered' => false,
            'cargoCategory'   => 'Товары народного потребления',
            'receiverAddress' => [
                'name'        => $order->recipient_name,
                'countryCode' => 'RU',
                'zipCode'     => $order->shipping_zip,
                'city'        => $order->shipping_city,
                'street'      => $order->shipping_street,
                'house'       => $order->shipping_house,
                'flat'        => $order->shipping_flat ?? '',
                'contactFio'  => $order->recipient_name,
                'contactPhone'=> $order->recipient_phone,
                'contactEmail'=> $order->recipient_email,
            ],
            'terminalCode'    => $order->dpd_terminal_code ?? null,
        ]],
    ]);

    $result = $response->return[0] ?? null;

    if (!$result || $result->status !== 'OK') {
        throw new DpdOrderException(
            'DPD order creation failed: ' . ($result->errorMessage ?? 'unknown error')
        );
    }

    return [
        'order_num'  => $result->orderNum,
        'dpd_order_num' => $result->orderNumberInternal,
    ];
}

Отслеживание

public function trackParcel(string $dpdOrderNum): array
{
    $response = $this->client->getStatesByClient([
        'clientOrderNr' => $dpdOrderNum,
    ]);

    return collect($response->return ?? [])
        ->map(fn($s) => [
            'date'    => $s->transitionTime,
            'status'  => $s->newState,
            'city'    => $s->terminalCityName ?? '',
            'comment' => $s->comment ?? '',
        ])
        ->toArray();
}

Печать накладных

DPD возвращает этикетки в формате PDF или ZPL (для термопринтеров):

public function printLabel(string $dpdOrderNum, string $format = 'PDF'): string
{
    $response = $this->client->createLabelFile([
        'OrderRanges' => [
            'clientOrderNr' => $dpdOrderNum,
        ],
        'fileFormat'  => $format, // PDF, ZPL
        'pageSize'    => 'A5',    // A4, A5, A6
        'printMode'   => 'TWO_SIDED',
    ]);

    return base64_decode($response->return->fileContent);
}

Вызов курьера для забора

public function createPickupRequest(
    string $date,         // 'Y-m-d'
    string $timeFrom,     // '10:00'
    string $timeTo,       // '18:00'
    int    $parcelCount,
    float  $totalWeight
): string {
    $response = $this->client->addPickupAsync([
        'pickupDate'  => $date . 'T00:00:00',
        'readyTime'   => $timeFrom,
        'closeTime'   => $timeTo,
        'addInfo'     => "Посылок: $parcelCount, вес: {$totalWeight} кг",
        'senderAddress' => [/* адрес склада */],
    ]);

    return $response->return->pickupRequestNumber ?? '';
}

Типичные проблемы интеграции

SOAP-ответы DPD иногда возвращают ошибки не через HTTP-статус, а в теле с status !== 'OK'. Необходима проверка каждого объекта ответа. Коды городов — не КЛАДР и не ФИАС, нужен собственный маппинг или поиск через их API при каждом запросе.

Сроки

Базовая интеграция — расчёт, ПВЗ, создание заказов — 5–7 дней. SOAP-специфика и тестирование добавляют 1–2 дня по сравнению с REST-шлюзами.