Интеграция 1С-Битрикс со службой доставки ПЭК
ПЭК (Первая Экспедиционная Компания) — одна из ключевых транспортных компаний для грузовой и сборной доставки в России. Основная специфика интеграции: ПЭК работает с грузами от 1 кг и выше, API заточен под параметры грузоместа (длина, ширина, высота, вес каждого места отдельно), а не под стандартный розничный формат Битрикс. Если у вас интернет-магазин с крупногабаритом — диваны, строительные материалы, промышленное оборудование — ПЭК часто становится основным перевозчиком.
API ПЭК: особенности
ПЭК предоставляет REST API с авторизацией через Bearer-токен. Токен получается через POST /v2/sign-in с логином и паролем из личного кабинета. Токен не имеет жёстко ограниченного TTL (в практике живёт 24–48 часов), но рекомендуется кэшировать и обновлять при получении 401.
Ключевые эндпоинты:
-
POST /v2/calculator— расчёт стоимости и сроков -
POST /v2/orders— создание заказа -
GET /v2/orders/{id}— статус заказа -
GET /v2/departments— список терминалов ПЭК
Базовый URL: https://api.pek.ru. Документация доступна в личном кабинете партнёра.
Расчёт стоимости
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('Расчёт недоступен'));
return $result;
}
$result->setDeliveryPrice((float)$response['price']);
$result->setPeriodDescription($response['period_min'] . '–' . $response['period_max'] . ' дней');
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
{
// ПЭК требует параметры каждого грузоместа отдельно
$weight = max($shipment->getWeight() / 1000, 1); // г → кг, минимум 1 кг
return [
[
'weight' => $weight,
'length' => (int)$this->getOption('DEFAULT_LENGTH', 50),
'width' => (int)$this->getOption('DEFAULT_WIDTH', 50),
'height' => (int)$this->getOption('DEFAULT_HEIGHT', 50),
],
];
}
}
Важно: ПЭК рассчитывает по объёмному весу. Если фактический вес меньше объёмного (Д×Ш×В / 5000 для авиа, / 4000 для авто), считается объёмный. Для крупногабарита это критично — передавайте реальные габариты товаров.
Получение cityId
ПЭК использует собственные числовые идентификаторы городов. Поиск города:
public function findCityId(string $cityName): ?int
{
$response = $this->apiGet('/v2/city?name=' . urlencode($cityName), $this->getApiToken());
return $response[0]['id'] ?? null;
}
Альтернатива: выгрузить справочник городов ПЭК и хранить маппинг название_города → pek_city_id в инфоблоке или кастомной таблице. При старте рекомендуется именно этот подход — API поиска города возвращает неоднозначные результаты для населённых пунктов с одинаковым названием.
Создание заказа и терминалы
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'] ?? '');
}
При типе door_terminal или terminal_door нужно передавать идентификатор терминала ПЭК. Список терминалов: GET /v2/departments. Фильтрация по cityId. На сайте реализуется выпадающий список терминалов с картой — виджет или кастомная реализация на Leaflet/Yandex Maps.
Кейс: магазин стройматериалов
Клиент — оптово-розничный магазин стройматериалов, средний вес заказа 50–200 кг, много листового материала. Проблема при внедрении: Битрикс хранит вес отправления как одно число, а ПЭК при заказе с несколькими позициями разных размеров требует список грузомест с габаритами каждого. Пришлось реализовать логику разбивки: у каждого товара в инфоблоке заведены свойства DELIVERY_LENGTH, DELIVERY_WIDTH, DELIVERY_HEIGHT. При формировании отправки каждая единица товара превращается в отдельное грузоместо.
Это увеличило точность расчёта (расхождение с реальной стоимостью сократилось с ±30% до ±5%) и устранило доначисления при получении груза.
Трекинг статусов
| Статус ПЭК | Значение |
|---|---|
accepted |
Принят к перевозке |
in_transit |
В пути |
arrived |
Прибыл на терминал назначения |
out_for_delivery |
Передан курьеру |
delivered |
Доставлен |
returned |
Возврат |
Вебхуков у ПЭК нет — только polling. Агент Битрикс раз в 4 часа запрашивает статусы активных отправлений через GET /v2/orders/{id} и обновляет статус заказа в магазине.
Сроки
| Состав | Срок |
|---|---|
| Расчёт стоимости + создание заказа | 4–5 дней |
| + Список терминалов + карта | +2–3 дня |
| + Разбивка на грузоместа по товарам | +2 дня |
| + Polling статусов + уведомления | +2 дня |







