1C-Bitrix Integration with DPD Delivery Service
DPD is an international courier service with strong coverage in Russia and Europe. It differs from most Russian carriers by using a SOAP interface instead of REST. This is not a problem, but requires a different approach: working with WSDL, SoapClient, and a specific data format. Integration with 1C-Bitrix is implemented through the standard custom delivery service mechanism.
DPD API: SOAP Interface
DPD provides several WSDL services:
-
https://ws.dpd.ru/services/calculator2?wsdl— cost calculation -
https://ws.dpd.ru/services/geography2?wsdl— cities and pickup points -
https://ws.dpd.ru/services/order2?wsdl— order creation -
https://ws.dpd.ru/services/tracking2?wsdl— tracking
Authorization is identical for all services: the client number (clientNumber) and key (clientKey) are passed in every request as part of the auth structure.
private function createSoapClient(string $wsdl): \SoapClient
{
return new \SoapClient($wsdl, [
'soap_version' => SOAP_1_1,
'trace' => true,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_DISK,
'encoding' => 'utf-8',
]);
}
private function getAuth(): array
{
return [
'clientNumber' => $this->getOption('CLIENT_NUMBER'),
'clientKey' => $this->getOption('CLIENT_KEY'),
];
}
WSDL caching (WSDL_CACHE_DISK) is mandatory — without it, every request downloads the schema, which is critical for cost calculation on the checkout page.
Delivery Cost Calculation
private function calcCost(
string $fromCityCode,
string $toCityCode,
int $weightGram
): float {
$client = $this->createSoapClient(
'https://ws.dpd.ru/services/calculator2?wsdl'
);
$request = [
'auth' => $this->getAuth(),
'pickup' => ['cityId' => (int)$fromCityCode],
'delivery' => ['cityId' => (int)$toCityCode],
'selfPickup' => false,
'selfDelivery' => false,
'weight' => $weightGram / 1000, // grams → kg (float)
'volume' => 0.001, // minimum volume
];
$response = $client->getServiceCost2($request);
$services = $response->return ?? [];
// Find the "DPD Classic" tariff (serviceCode = 'CUR')
foreach ((array)$services as $service) {
if ($service->serviceCode === 'CUR') {
return $service->cost;
}
}
return 0;
}
DPD returns the cost for all available tariffs. Main ones: CUR (DPD Classic — door-to-door delivery), PCL (DPD Online Express), ECO (DPD Economy). The tariff code is selected in the module settings.
DPD City Mapping
DPD uses its own city identifiers (cityId). Conversion from city name or CLADR code:
public function findCity(string $cityName): ?int
{
$client = $this->createSoapClient(
'https://ws.dpd.ru/services/geography2?wsdl'
);
// Cache the result for 24 hours
$cacheKey = 'dpd_city_' . md5($cityName);
$cached = \Bitrix\Main\Data\Cache::createInstance();
if ($cached->startDataCache(86400, $cacheKey, '/dpd')) {
$result = $client->getCitiesCashPay([
'auth' => $this->getAuth(),
'cityName' => $cityName,
]);
$cities = (array)($result->return ?? []);
$cityId = !empty($cities) ? $cities[0]->cityId : null;
$cached->endDataCache(['cityId' => $cityId]);
} else {
$cityId = $cached->getVars()['cityId'];
}
return $cityId;
}
The 24-hour cache is critical: the DPD city list does not change hourly, and without caching every cost calculation makes two SOAP requests.
Creating a DPD Order
public function createOrder(\Bitrix\Sale\Shipment $shipment): string
{
$client = $this->createSoapClient(
'https://ws.dpd.ru/services/order2?wsdl'
);
$order = $shipment->getOrder();
$props = $order->getPropertyCollection();
$request = [
'auth' => $this->getAuth(),
'header' => [
'datePickup' => date('Y-m-d', strtotime('+1 day')),
'senderAddress' => $this->getSenderAddress(),
'pickupTimePeriod' => '9:00-18:00',
],
'order' => [[
'orderNumberInternal' => (string)$order->getId(),
'serviceCode' => 'CUR',
'serviceVariant' => 'ДД', // door-to-door
'weight' => $this->getWeight($shipment) / 1000,
'declaredValue' => $order->getPrice(),
'cargoRegistered' => false,
'cargoCategory' => 'Товар',
'receiverName' => $props->getItemByOrderPropertyCode('FIO')?->getValue(),
'receiverPhone' => $props->getItemByOrderPropertyCode('PHONE')?->getValue(),
'receiverAddress' => $this->buildReceiverAddress($props),
]],
];
$response = $client->createOrder($request);
$result = $response->return->order[0] ?? null;
return $result->orderNum ?? '';
}
Tracking
public function trackOrder(string $dpdOrderNum): array
{
$client = $this->createSoapClient(
'https://ws.dpd.ru/services/tracking2?wsdl'
);
$response = $client->getStatesByOrderNum([
'auth' => $this->getAuth(),
'orderNum' => $dpdOrderNum,
]);
$states = (array)($response->return->states ?? []);
$last = end($states);
return [
'status' => $last->newState ?? '',
'city' => $last->city ?? '',
'timestamp' => $last->transitionTime ?? '',
];
}
DPD does not provide push webhooks. Only polling — a 1C-Bitrix agent runs every 2–3 hours for active orders.
Timelines
| Scope | Timeline |
|---|---|
| SOAP client + calculation + city mapping | 3–4 days |
| + Order creation + tracking polling | +2 days |
| + DPD pickup points on map | +2 days |







