Покупатель добавляет товары в корзину, переходит к оформлению и видит неожиданно высокую стоимость доставки. По данным исследований, неожиданная стоимость доставки — причина 60% брошенных корзин. Калькулятор доставки, предварительно показывающий стоимость, решает эту проблему и повышает конверсию на 15–30%. Реализация такого калькулятора — нетривиальная задача: нужно учесть объёмный вес, интегрироваться с API нескольких служб доставки (СДЭК, Boxberry, Почта России), обеспечить отказоустойчивость и скорость расчётов. Запросы к API занимают 200–800 мс и часто платные, поэтому требуется грамотное кеширование и параллельная обработка. Мы рассмотрим технические детали: от хранения тарифов в базе до параллельных запросов и обработки ошибок. Приведём примеры кода на Laravel и React, расскажем о кешировании и работе с объёмным весом. Внедрение агрегатора окупается за 2–3 месяца, экономя значительную сумму на доставке.
Наша команда имеет 5+ лет опыта в e-commerce разработке и реализовала 20+ калькуляторов доставки. Это позволило накопить типовые решения, ускоряющие внедрение. Обращайтесь — мы поможем подобрать оптимальную архитектуру под ваш бюджет.
Что считает калькулятор
Стоимость доставки зависит от параметров, которые нужно получить из разных источников:
- Адрес склада (откуда везут) — может быть фиксированным или ближайшим магазином.
- Адрес покупателя (куда везут) — до двери или до пункта выдачи.
- Вес и габариты товаров в корзине (что везут).
- Выбранный способ доставки — курьер, ПВЗ, постамат, Почта России.
- Срочность — стандарт или экспресс.
Параметры товаров хранятся в базе данных интернет-магазина. Тарифы доставки — либо в собственных таблицах (для партнёрских договоров с фиксированными ценами), либо приходят в реальном времени через API службы доставки.
Локальные таблицы и API: два подхода к расчёту
Локальные тарифные таблицы
Для простых случаев — когда есть договор с фиксированными ценами или доставка своими силами — тарифы хранятся локально:
CREATE TABLE shipping_zones ( id SERIAL PRIMARY KEY, name VARCHAR(100), regions TEXT[], base_price DECIMAL(10,2), price_per_kg DECIMAL(10,2), price_per_km DECIMAL(10,2), min_days INT, max_days INT ); CREATE TABLE shipping_methods ( id SERIAL PRIMARY KEY, zone_id INT REFERENCES shipping_zones(id), name VARCHAR(100), carrier VARCHAR(50), multiplier DECIMAL(4,2) DEFAULT 1.0, free_from DECIMAL(10,2) ); class LocalShippingCalculator { public function calculate(Cart $cart, Address $destination): Collection { $zone = $this->zoneDetector->detect($destination->city); $weight = $cart->totalWeight(); $orderTotal = $cart->total(); return ShippingMethod::where('zone_id', $zone->id)->get() ->map(function (ShippingMethod $method) use ($weight, $orderTotal, $zone) { $cost = $zone->base_price + ($weight * $zone->price_per_kg); $cost *= $method->multiplier; if ($method->free_from && $orderTotal >= $method->free_from) { $cost = 0; } return [ 'id' => $method->id, 'name' => $method->name, 'carrier' => $method->carrier, 'cost' => round($cost, 2), 'min_days' => $zone->min_days * $method->multiplier < 1 ? 1 : (int)($zone->min_days / $method->multiplier), 'max_days' => $zone->max_days, 'free' => $cost === 0.0, ]; }); } } API-расчёт в реальном времени: пример с СДЭК
Отметим: когда нужны актуальные тарифы службы доставки, запрос уходит в их API:
class CdekShippingCalculator { private string $baseUrl = 'https://api.cdek.ru/v2'; public function calculate( string $fromCity, string $toCity, float $weight, array $dimensions ): array { $token = $this->authenticate(); $response = Http::withToken($token) ->post("{$this->baseUrl}/calculator/tarifflist", [ 'from_location' => ['city' => $fromCity], 'to_location' => ['city' => $toCity], 'packages' => [[ 'weight' => (int)($weight * 1000), 'length' => $dimensions['length'], 'width' => $dimensions['width'], 'height' => $dimensions['height'], ]], ]); return collect($response->json('tariff_codes')) ->map(fn($t) => [ 'tariff_code' => $t['tariff_code'], 'tariff_name' => $t['tariff_name'], 'cost' => $t['delivery_sum'], 'min_days' => $t['period_min'], 'max_days' => $t['period_max'], ]) ->toArray(); } } Сравнение подходов
| Критерий | Локальные таблицы | API в реальном времени |
|---|---|---|
| Скорость расчёта | <10 мс | 200–800 мс |
| Актуальность тарифов | Ручное обновление | Автоматически |
| Сложность интеграции | Низкая | Средняя |
| Стоимость обслуживания | Низкая | Средняя (комиссии API) |
Локальные таблицы быстрее API в 10–80 раз, но уступают в гибкости. Выбор зависит от объёмов и требований к актуальности.
Как агрегировать несколько служб доставки?
Реальный калькулятор обычно показывает варианты от нескольких служб одновременно. Запросы уходят параллельно. Если один сервис недоступен — показываем остальные. Покупатель не видит ошибку, только меньше вариантов.
public function getShippingOptions(Cart $cart, Address $address): array { $weight = $cart->totalWeight(); $dimensions = $cart->boundingBox(); $results = collect([ 'cdek' => fn() => $this->cdek->calculate($address, $weight, $dimensions), 'boxberry' => fn() => $this->boxberry->calculate($address, $weight, $dimensions), 'pochta' => fn() => $this->russianPost->calculate($address, $weight, $dimensions), ])->map(function ($calculator, $carrier) { try { return $calculator(); } catch (\Exception $e) { logger()->warning("Shipping calculator error: $carrier", ['error' => $e->getMessage()]); return []; } })->flatten(1)->sortBy('cost')->values(); return $results->toArray(); } Ниже — сравнение популярных служб доставки для интеграции.
| Служба | Скорость доставки (дни) | Зоны покрытия | Сложность API | Комиссия |
|---|---|---|---|---|
| СДЭК | 1–5 | Города РФ, СНГ | Низкая | По тарифам |
| Boxberry | 2–7 | Города РФ, СНГ | Средняя | По тарифам |
| Почта России | 3–14 | Все регионы РФ | Высокая | Низкая |
Что такое объёмный вес и как он влияет на стоимость?
Многие службы считают оплачиваемый вес как максимум из фактического и объёмного. Согласно правилам СДЭК, оплачиваемый вес равен максимуму из фактического и объёмного. Объёмный вес — это расчётный вес, основанный на габаритах. Для воздушной доставки коэффициент 6000 см³/кг, для морской — 1000 см³/кг. Если этого не учесть, расценки будут занижены. Пример: посылка весом 2 кг с габаритами 50×40×30 см даёт объёмный вес 60000/5000=12 кг — платить придётся за 12 кг.
Оптимизация расчётов через кеширование
Запросы к API служб доставки медленные (200–800 мс) и часто платные. Кешируем по ключу из города отправления, города назначения и веса. Тарифы меняются редко, поэтому кеш на 30 минут — оптимален. При обновлении тарифов инвалидируем по паттерну.
Интерфейс калькулятора: реактивный компонент
На странице товара или корзины — компактный блок с полем ввода города и списком методов с ценами и сроками. Без перезагрузки страницы:
const ShippingCalculator = () => { const [city, setCity] = useState(''); const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); const calculate = useMemo( () => debounce(async (cityValue) => { if (cityValue.length < 3) return; setLoading(true); try { const res = await fetch('/api/shipping/calculate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ city: cityValue, cart_id: cartId }), }); const data = await res.json(); setOptions(data.options); } finally { setLoading(false); } }, 600), [cartId] ); return ( <div className="shipping-calculator"> <input value={city} onChange={(e) => { setCity(e.target.value); calculate(e.target.value); }} placeholder="Введите ваш город" /> {loading && <Spinner />} {options.map((opt) => ( <ShippingOption key={opt.id} option={opt} /> ))} </div> ); }; Дебаунс на 600 мс — не стреляем запросами после каждого символа.
Сколько времени занимает разработка?
- Аналитика — сбор требований, определение списка служб доставки, тарифных зон.
- Проектирование — выбор стека (Laravel/PHP + React/Next.js), проектирование схемы базы.
- Реализация — написание калькулятора, интеграция с API, разработка UI-компонента.
- Тестирование — проверка 100+ сценариев (разные города, веса, ошибки API).
- Деплой и мониторинг — настройка кеширования, алёртов при сбоях.
Ориентировочные сроки:
- Калькулятор с одной службой по фиксированным тарифам — 2–3 дня.
- С реальным API одной службы — 3–5 дней (включая обработку ошибок и кеширование).
- Агрегатор на 3–5 служб с интерфейсом выбора — 2–3 недели.
Экономия клиентов на доставке после внедрения агрегатора составляет в среднем 20–30%, а инвестиции окупаются за 2–3 месяца. При значительном обороте экономия может быть существенной. Свяжитесь с нами для предварительной оценки вашего проекта.
Что входит в результат
- Документация: описание тарифных схем, схема базы, инструкция по добавлению новых служб.
- Доступы к панели администратора для управления тарифами.
- Обучение сотрудников: как изменять тарифы, добавлять зоны.
- Техническая поддержка в течение месяца после запуска.
Получите консультацию инженера: мы поможем выбрать оптимальную архитектуру и реализуем калькулятор за 2–3 недели.







