Integration of 1C-Bitrix with the PEK delivery service

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

1C-Bitrix Integration with PEK Delivery Service

PEK (Pervaya Ekspeditsionnaya Kompaniya / First Expeditionary Company) is one of the key transport companies for cargo and consolidated freight delivery in Russia. The main integration specifics: PEK works with cargo from 1 kg and above, the API is tailored for cargo space parameters (length, width, height, and weight of each unit separately) rather than the standard retail format of 1C-Bitrix. If your online store sells oversized goods — sofas, building materials, industrial equipment — PEK often becomes the primary carrier.

PEK API: Specifics

PEK provides a REST API with Bearer token authorization. The token is obtained via POST /v2/sign-in with the login and password from your personal account. The token has no hard TTL (in practice it lives 24–48 hours), but it is recommended to cache it and refresh upon receiving a 401 response.

Key endpoints:

  • POST /v2/calculator — cost and delivery time calculation
  • POST /v2/orders — order creation
  • GET /v2/orders/{id} — order status
  • GET /v2/departments — list of PEK terminals

Base URL: https://api.pek.ru. Documentation is available in the partner personal account.

Cost Calculation

class PekDeliveryHandler extends \Bitrix\Sale\Delivery\Services\Base
{
    protected function calculateConcrete(
        \Bitrix\Sale\Shipment $shipment
    ): \Bitrix\Sale\Delivery\CalculationResult {
        $result = new \Bitrix\Sale\Delivery\CalculationResult();

        $token   = $this->getApiToken();
        $payload = $this->buildCalcPayload($shipment);

        $response = $this->apiPost('/v2/calculator', $payload, $token);

        if (empty($response['price'])) {
            $result->addError(new \Bitrix\Main\Error('Calculation unavailable'));
            return $result;
        }

        $result->setDeliveryPrice((float)$response['price']);
        $result->setPeriodDescription($response['period_min'] . '–' . $response['period_max'] . ' days');

        return $result;
    }

    private function buildCalcPayload(\Bitrix\Sale\Shipment $shipment): array
    {
        $order = $shipment->getOrder();

        return [
            'senderCityId'    => (int)$this->getOption('SENDER_CITY_ID'),
            'receiverCityId'  => $this->getReceiverCityId($shipment),
            'cargo'           => $this->buildCargoPlaces($shipment),
            'service'         => $this->getOption('SERVICE_TYPE', 'door_door'),
            'declaredValue'   => round($order->getPrice(), 2),
        ];
    }

    private function buildCargoPlaces(\Bitrix\Sale\Shipment $shipment): array
    {
        // PEK requires individual parameters for each cargo unit
        $weight = max($shipment->getWeight() / 1000, 1); // g → kg, minimum 1 kg
        return [
            [
                'weight' => $weight,
                'length' => (int)$this->getOption('DEFAULT_LENGTH', 50),
                'width'  => (int)$this->getOption('DEFAULT_WIDTH', 50),
                'height' => (int)$this->getOption('DEFAULT_HEIGHT', 50),
            ],
        ];
    }
}

Important: PEK calculates based on volumetric weight. If the actual weight is less than the volumetric weight (L×W×H / 5000 for air, / 4000 for road), the volumetric weight is used. For oversized goods this is critical — provide the actual dimensions of the items.

Obtaining cityId

PEK uses its own numeric city identifiers. City search:

public function findCityId(string $cityName): ?int
{
    $response = $this->apiGet('/v2/city?name=' . urlencode($cityName), $this->getApiToken());
    return $response[0]['id'] ?? null;
}

Alternative: export the PEK city directory and store the mapping city_name → pek_city_id in an infoblock or a custom table. This approach is recommended for initial setup — the city search API returns ambiguous results for localities with identical names.

Order Creation and Terminals

private function createPekOrder(\Bitrix\Sale\Shipment $shipment): string
{
    $order = $shipment->getOrder();
    $props = $order->getPropertyCollection();

    $payload = [
        'senderCityId'   => (int)$this->getOption('SENDER_CITY_ID'),
        'receiverCityId' => $this->getReceiverCityId($shipment),
        'cargo'          => $this->buildCargoPlaces($shipment),
        'service'        => $this->getOption('SERVICE_TYPE', 'door_door'),
        'declaredValue'  => round($order->getPrice(), 2),
        'sender' => [
            'company' => $this->getOption('SENDER_COMPANY'),
            'contact' => $this->getOption('SENDER_CONTACT'),
            'phone'   => $this->getOption('SENDER_PHONE'),
            'address' => $this->getOption('SENDER_ADDRESS'),
        ],
        'receiver' => [
            'contact' => $props->getItemByOrderPropertyCode('FIO')?->getValue(),
            'phone'   => $props->getItemByOrderPropertyCode('PHONE')?->getValue(),
            'address' => $props->getItemByOrderPropertyCode('ADDRESS')?->getValue(),
        ],
    ];

    $response = $this->apiPost('/v2/orders', $payload, $this->getApiToken());
    return (string)($response['id'] ?? '');
}

For door_terminal or terminal_door service types, the PEK terminal identifier must be provided. Terminal list: GET /v2/departments. Filter by cityId. The website implements a terminal dropdown with a map — a widget or custom implementation using Leaflet/Yandex Maps.

Case Study: Building Materials Store

Client — a wholesale and retail building materials store, average order weight 50–200 kg, many sheet materials. Problem during implementation: 1C-Bitrix stores shipment weight as a single number, while PEK for orders with multiple items of different sizes requires a list of cargo units with individual dimensions. A splitting logic had to be implemented: each product in the infoblock has properties DELIVERY_LENGTH, DELIVERY_WIDTH, DELIVERY_HEIGHT. When forming a shipment, each unit of goods becomes a separate cargo unit.

This increased calculation accuracy (discrepancy with actual cost reduced from ±30% to ±5%) and eliminated surcharges upon cargo receipt.

Status Tracking

PEK Status Meaning
accepted Accepted for shipment
in_transit In transit
arrived Arrived at destination terminal
out_for_delivery Handed to courier
delivered Delivered
returned Returned

PEK has no webhooks — polling only. A 1C-Bitrix agent runs every 4 hours, querying statuses of active shipments via GET /v2/orders/{id} and updating the order status in the store.

Timelines

Scope Timeline
Cost calculation + order creation 4–5 days
+ Terminal list + map +2–3 days
+ Per-item cargo unit splitting +2 days
+ Status polling + notifications +2 days