Покупатель добавляет товары в корзину, переходит к оформлению и видит неожиданно высокую стоимость доставки. По данным исследований, неожиданная стоимость доставки — причина 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 недели.







