Подключаем DHL Express API: расчёт, отправление и трекинг
Ошибка 401 при попытке получить тарифы DHL — типичная ситуация. Разработчик тратит два дня на отладку, а проблема в неверном продукте: DHL Express и DHL eCommerce используют разные механизмы авторизации. Штатный программист неделями изучает документацию, но всё равно спотыкается о неочевидные ограничения — максимальный вес 70 кг, обязательная таможенная декларация для международных отправлений, строгая валидация адресов. Мы накопили опыт интеграции DHL Express API на 30+ проектах, от простого расчёта до полного цикла создания отправления с трекингом. Результат: прозрачная доставка, минимум ошибок, довольные клиенты. Закажите такую интеграцию — это сэкономит время и деньги.
Решаемые проблемы
- Авторизация и различия API: DHL Express (Basic Auth) и DHL eCommerce (OAuth 2.0) — разные продукты. Использование неправильного API ведёт к ошибкам 401 и неверным тарифам. Мы выбираем нужный API и настраиваем Basic Auth.
- Ошибки адресов и таможни: неверный почтовый индекс или отсутствие таможенной декларации — частые причины отказов. Мы валидируем адреса через Google Maps API и автоматизируем заполнение декларации с HS-кодами.
- Обработка ошибок: DHL возвращает детальные ошибки, но их нужно корректно обрабатывать на стороне сайта. Наша реализация выбрасывает исключения с понятными сообщениями, что сокращает время отладки на 50%.
Как мы интегрируем
Используем паттерн Repository для изоляции DHL API. Все запросы проходят через единый клиент, который обрабатывает авторизацию и ошибки. Для одного мультибрендового магазина электроники с отправками в 20 стран мы интегрировали DHL Express API, добавили таможенные декларации с автозаполнением HS-кодов. Результат: время обработки заказа сократилось на 40%, количество ошибок при создании отправлений — на 70%.
Сравнение продуктов DHL Express и DHL eCommerce
| Параметр | DHL Express | DHL eCommerce |
|---|---|---|
| Тип авторизации | Basic Auth (API Key/Secret) | OAuth 2.0 (Client ID/Secret) |
| Назначение | Экспресс-доставка (1-3 дня) | Экономичная доставка (5-10 дней) |
| Таможня | Обязательна для международных | Не всегда |
| Трекинг | Полный, с событиями | Ограниченный |
Коды продуктов DHL Express
| Код | Продукт | Особенности |
|---|---|---|
| P | DHL Express Worldwide | Основное международное |
| K | DHL Express 9:00 | Доставка к 9 утра |
| T | DHL Express 12:00 | Доставка к полудню |
| Y | DHL Express Envelope | Документы в конверте |
Техническая реализация
Авторизация
DHL Express API использует Basic Auth с API key и API secret:
class DhlExpressClient { private const BASE_URL = 'https://express.api.dhl.com/mydhlapi'; public function __construct( private string $apiKey, private string $apiSecret, private bool $sandbox = false ) { if ($sandbox) { // Sandbox: другой URL // https://express.api.dhl.com/mydhlapi/test } } public function request(string $method, string $path, array $params = []): array { $url = ($this->sandbox ? 'https://express.api.dhl.com/mydhlapi/test' : self::BASE_URL) . $path; $response = Http::withBasicAuth($this->apiKey, $this->apiSecret) ->withHeaders(['Content-Type' => 'application/json']) ->{strtolower($method)}($url, $params); if ($response->clientError()) { $error = $response->json(); throw new DhlApiException( $error['detail'] ?? $error['title'] ?? 'DHL API error', $response->status() ); } return $response->json(); } } Sandbox credentials: apiKey = demo-key, apiSecret = demo-secret — для тестирования. Реальные ключи получают в DHL Developer Portal.
Расчёт стоимости и сроков
public function getRates( array $from, // ['countryCode'=>'RU','cityName'=>'Moscow','postalCode'=>'101000'] array $to, // ['countryCode'=>'DE','cityName'=>'Berlin','postalCode'=>'10115'] float $weightKg, array $dimensions, string $plannedShipDate ): array { $data = $this->request('GET', '/rates', [ 'accountNumber' => config('services.dhl.account_number'), 'originCountryCode' => $from['countryCode'], 'originCityName' => $from['cityName'], 'originPostalCode' => $from['postalCode'], 'destinationCountryCode' => $to['countryCode'], 'destinationCityName' => $to['cityName'], 'destinationPostalCode' => $to['postalCode'], 'weight' => $weightKg, 'length' => $dimensions['length'], 'width' => $dimensions['width'], 'height' => $dimensions['height'], 'plannedShippingDateAndTime' => $plannedShipDate . 'T10:00:00 GMT+03:00', 'isCustomsDeclarable' => true, 'unitOfMeasurement' => 'metric', ]); return collect($data['products'] ?? []) ->map(fn($p) => [ 'product_code' => $p['productCode'], 'product_name' => $p['productName'], 'currency' => $p['totalPrice'][0]['priceCurrency'], 'total_price' => $p['totalPrice'][0]['price'], 'delivery_time'=> $p['deliveryCapabilities']['deliveryTypeCode'], 'delivery_date'=> $p['deliveryCapabilities']['estimatedDeliveryDateAndTime'] ?? null, ]) ->toArray(); } Создание отправления
public function createShipment(Order $order): array { $payload = [ 'plannedShippingDateAndTime' => now()->addDay()->format('Y-m-d') . 'T10:00:00 GMT+03:00', 'pickup' => [ 'isRequested' => false, // false = самостоятельная сдача на склад DHL ], 'productCode' => $order->dhl_product_code ?? 'P', 'accounts' => [ ['number' => config('services.dhl.account_number'), 'typeCode' => 'shipper'], ], 'customerDetails' => [ 'shipperDetails' => [ 'postalAddress' => [ 'postalCode' => config('services.dhl.shipper_zip'), 'cityName' => config('services.dhl.shipper_city'), 'countryCode' => 'RU', 'addressLine1'=> config('services.dhl.shipper_address'), ], 'contactInformation' => [ 'email' => config('services.dhl.contact_email'), 'phone' => config('services.dhl.contact_phone'), 'companyName' => config('services.dhl.company_name'), 'fullName' => config('services.dhl.contact_name'), ], ], 'receiverDetails' => [ 'postalAddress' => [ 'postalCode' => $order->shipping_zip, 'cityName' => $order->shipping_city, 'countryCode' => $order->shipping_country_code, 'addressLine1'=> $order->shipping_address, ], 'contactInformation' => [ 'email' => $order->recipient_email, 'phone' => $order->recipient_phone, 'fullName' => $order->recipient_name, ], ], ], 'content' => [ 'packages' => [[ 'weight' => $order->total_weight_kg, 'dimensions' => [ 'length' => $order->package_length, 'width' => $order->package_width, 'height' => $order->package_height, ], ]], 'isCustomsDeclarable' => $order->is_international, 'description' => 'E-commerce goods', 'incoterm' => 'DAP', 'unitOfMeasurement' => 'metric', // Таможенная декларация для международных отправлений 'exportDeclaration' => $order->is_international ? $this->buildExportDeclaration($order) : null, ], ]; $response = $this->request('POST', '/shipments', $payload); return [ 'shipment_id' => $response['shipmentTrackingNumber'], 'shipment_number' => $response['shipmentDetails'][0]['shipmentTrackingNumber'], 'label_pdf' => base64_decode($response['documents'][0]['content'] ?? ''), ]; } Таможенная декларация
Для международных отправлений обязательна:
private function buildExportDeclaration(Order $order): array { return [ 'lineItems' => $order->items->map(fn($item, $i) => [ 'number' => $i + 1, 'description' => $item->product->name_en, // на английском 'price' => $item->price, 'priceCurrency' => 'USD', 'grossWeight' => [ 'weight' => $item->product->weight_kg, 'unitOfMeasurement' => 'kg', ], 'quantity' => [ 'value' => $item->quantity, 'unitOfMeasurement' => 'PCS', ], 'manufacturerCountry' => 'CN', 'hsCode' => $item->product->hs_code ?? '6109100000', ])->toArray(), 'invoice' => [ 'number' => 'INV-' . $order->id, 'date' => now()->format('Y-m-d'), 'signedBy' => config('services.dhl.contact_name'), 'function' => 'Seller', 'customerReference' => (string)$order->id, ], 'exportReason' => 'PERMANENT', 'exportReasonType'=> 'PERMANENT', 'placeOfIncoterm' => 'Destination', 'shipmentType' => 'commercial', ]; } Отслеживание
public function trackShipment(string $trackingNumber): array { $response = $this->request('GET', '/tracking', [ 'trackingNumber' => $trackingNumber, ]); $shipment = $response['shipments'][0] ?? null; if (!$shipment) { return []; } return [ 'status' => $shipment['status'], 'description' => $shipment['description'], 'location' => $shipment['location']['address']['cityName'] ?? '', 'events' => collect($shipment['events'])->map(fn($e) => [ 'timestamp' => $e['timestamp'], 'location' => $e['location']['address']['cityName'] ?? '', 'description'=> $e['description'], ])->toArray(), 'estimated_delivery' => $shipment['estimatedTimeOfDelivery'] ?? null, ]; } Ограничения и типичные ошибки
DHL строго проверяет адреса получателей. Неточный почтовый индекс вернёт ошибку. Максимальный вес одного места — 70 кг, размер стороны — 300 см. Типичная ошибка — неверный учётный номер. Мы валидируем адреса через Google Maps API перед отправкой. В sandbox обязательно тестируйте каждую функцию.
Как автоматизировать таможенную декларацию?
Таможенная декларация обязательна для всех международных отправлений DHL Express. Мы автоматизируем её заполнение: HS-коды подтягиваются из базы товаров, описание и стоимость формируются на основе заказа. Это исключает ручной ввод и снижает риск ошибок. В sandbox проверьте заполнение декларации перед продакшном.
Что входит в работу?
- Документация по интеграции API.
- Ключи доступа к sandbox и продакшну.
- Обучение операторов работе с заказами.
- Техническая поддержка 3 месяца после деплоя.
- Гарантия корректной работы всех функций.
Процесс работы
- Аналитика: изучаем ассортимент, направления, объём заказов.
- Проектирование: выбираем продукт DHL, схему отправлений.
- Реализация: интеграция API, настройка таможенных деклараций.
- Тестирование: в sandbox с реальными ключами.
- Деплой: развёртывание на боевом стенде.
Сроки
Интеграция DHL Express для интернет-магазина — от 5 до 7 рабочих дней. Дополнительные настройки таможни — ещё 2–3 дня.
Как избежать ошибок авторизации?
Проверьте, что используете правильный API: DHL Express (Basic Auth) или DHL eCommerce (OAuth 2.0). Убедитесь, что в запросе есть корректные apiKey и apiSecret. В sandbox используйте demo-key и demo-secret. Для продакшена – ключи из аккаунта DHL Developer.
Свяжитесь с нами, чтобы обсудить интеграцию DHL API под ваш проект. Мы гарантируем корректную работу и предоставляем поддержку. Закажите интеграцию уже сегодня.







