Інтеграція служби доставки СДЭК на сайт
СДЭК — один з найбільших логістичних операторів Росії з покриттям більше 5400 міст, власною мережею ПВЗ і постаматів. API v2 дозволяє підключити розрахунок вартості, вибір ПВЗ на карті, створення замовлень на доставку й відстеження посилок прямо з вашого сайту.
Авторизація в API
СДЭК використовує OAuth 2.0 з client credentials grant. Токен живе 3600 секунд, потім потрібен новий:
class CdekAuthService
{
private const TOKEN_URL = 'https://api.cdek.ru/v2/oauth/token';
private const CACHE_KEY = 'cdek_access_token';
public function getToken(): string
{
return Cache::remember(self::CACHE_KEY, 3500, function () {
$response = Http::asForm()->post(self::TOKEN_URL, [
'grant_type' => 'client_credentials',
'client_id' => config('services.cdek.client_id'),
'client_secret' => config('services.cdek.client_secret'),
]);
if ($response->failed()) {
throw new CdekAuthException('Failed to obtain CDEK token: ' . $response->body());
}
return $response->json('access_token');
});
}
}
Для тестування використовуються окремі credentials:
-
client_id:EMscd6r9JnFiQ3bLoyjJY6eM -
client_secret:PjLZkKBHEiLK3YsjtNrt7ZpUzj - базовий URL:
https://api.edu.cdek.ru/v2
Розрахунок вартості доставки
class CdekCalculatorService
{
public function calculateTariffList(
string $fromCityCode,
string $toCityCode,
array $packages,
int $deliveryType = 1 // 1=дверь-дверь, 2=дверь-ПВЗ, 3=ПВЗ-дверь, 4=ПВЗ-ПВЗ
): array {
$response = Http::withToken($this->auth->getToken())
->post('https://api.cdek.ru/v2/calculator/tarifflist', [
'type' => $deliveryType,
'from_location' => ['code' => (int)$fromCityCode],
'to_location' => ['code' => (int)$toCityCode],
'packages' => $packages,
]);
if ($response->failed()) {
throw new CdekApiException($response->body());
}
return collect($response->json('tariff_codes'))
->filter(fn($t) => empty($t['errors']))
->map(fn($t) => [
'code' => $t['tariff_code'],
'name' => $t['tariff_name'],
'cost' => $t['delivery_sum'],
'min_days' => $t['period_min'],
'max_days' => $t['period_max'],
])
->values()
->toArray();
}
}
Основні тарифи: 136 — посилка склад-склад, 137 — посилка склад-дверь, 138 — посилка дверь-склад, 139 — посилка дверь-дверь.
Отримання коду міста СДЭК
СДЭК використовує власні коди міст, не збігаючись з ФИАС або КЛАДР. Пошук по назві:
public function findCityCode(string $cityName, string $countryCode = 'RU'): ?int
{
$response = Http::withToken($this->auth->getToken())
->get('https://api.cdek.ru/v2/location/cities', [
'country_codes' => [$countryCode],
'city' => $cityName,
'size' => 5,
]);
$cities = $response->json('0');
return $cities['code'] ?? null;
}
Рекомендується кешувати справочник міст локально — він змінюється рідко.
Список пунктів видачі
public function getPickupPoints(string $cityCode, float $weightKg): array
{
$response = Http::withToken($this->auth->getToken())
->get('https://api.cdek.ru/v2/deliverypoints', [
'city_code' => $cityCode,
'weight_max' => (int)($weightKg),
'type' => 'PVZ',
'is_handout' => 'true',
]);
return collect($response->json())
->map(fn($p) => [
'code' => $p['code'],
'name' => $p['name'],
'address' => $p['location']['address'],
'lat' => $p['location']['latitude'],
'lng' => $p['location']['longitude'],
'work_time' => $p['work_time'],
'cash_allowed'=> $p['have_cash'],
])
->toArray();
}
Створення замовлення
Після підтвердження покупки — реєструємо замовлення в СДЭК:
public function createOrder(Order $order): string
{
$payload = [
'tariff_code' => $order->cdek_tariff_code,
'from_location' => [
'code' => config('services.cdek.warehouse_city_code'),
'address' => config('services.cdek.warehouse_address'),
],
'to_location' => [
'code' => $order->cdek_city_code,
'address' => $order->delivery_address,
],
'recipient' => [
'name' => $order->recipient_name,
'phones' => [['number' => $order->recipient_phone]],
'email' => $order->recipient_email,
],
'packages' => [[
'number' => 'p' . $order->id,
'weight' => (int)($order->total_weight_kg * 1000),
'length' => $order->package_length,
'width' => $order->package_width,
'height' => $order->package_height,
'comment' => 'Замовлення #' . $order->id,
'items' => $order->items->map(fn($item) => [
'name' => $item->product->name,
'ware_key'=> (string)$item->product_id,
'cost' => $item->price,
'amount' => $item->quantity,
'weight' => (int)($item->product->weight_g),
])->toArray(),
]],
];
if ($order->pickup_point_code) {
$payload['delivery_point'] = $order->pickup_point_code;
}
$response = Http::withToken($this->auth->getToken())
->post('https://api.cdek.ru/v2/orders', $payload);
$orderId = $response->json('entity.uuid');
if (!$orderId) {
throw new CdekOrderException('Не вдалося створити замовлення СДЭК');
}
return $orderId;
}
Відстеження статусів через webhook
СДЭК уміє відправляти уведомлення про зміну статусу замовлення:
Http::withToken($token)->post('https://api.cdek.ru/v2/webhooks', [
'url' => 'https://yoursite.ru/api/cdek/webhook',
'type' => 'ORDER_STATUS',
]);
public function handleWebhook(Request $request): Response
{
$data = $request->json()->all();
if ($data['type'] !== 'ORDER_STATUS') {
return response('ok', 200);
}
$order = Order::where('cdek_uuid', $data['attributes']['uuid'])->first();
if ($order) {
$order->update([
'cdek_status' => $data['attributes']['status']['code'],
'cdek_status_at' => now(),
]);
if (in_array($data['attributes']['status']['code'], ['READY_FOR_PICKUP', 'DELIVERED'])) {
dispatch(new NotifyCustomerDeliveryStatus($order));
}
}
return response('ok', 200);
}
Ключові статуси: CREATED, ACCEPTED_AT_SENDER_WAREHOUSE, READY_FOR_PICKUP, DELIVERED, NOT_DELIVERED.
Друк накладних
public function getPrintForm(string $cdekUuid): string
{
$response = Http::withToken($this->auth->getToken())
->post('https://api.cdek.ru/v2/print/orders', [
'orders' => [['order_uuid' => $cdekUuid]],
'copy' => 1,
]);
$taskUuid = $response->json('entity.uuid');
for ($i = 0; $i < 10; $i++) {
sleep(2);
$status = Http::withToken($this->auth->getToken())
->get("https://api.cdek.ru/v2/print/orders/{$taskUuid}")
->json();
if ($status['entity']['status'] === 'READY') {
return $status['entity']['url'];
}
}
throw new \RuntimeException('Print form generation timeout');
}
Терміни й обсяг робіт
Базова інтеграція (розрахунок стоимости + ПВЗ на карті + створення замовлень) — 5–7 робочих днів. Додавання відстеження через webhook, печаті накладних, синхронізації статусів — ще 3–4 дні. Тестування в тестовій середовищі СДЭК перед переключенням на боєву — обов'язковий етап, займає 1–2 дні.







