A customer adds items to cart, proceeds to checkout, and sees unexpectedly high delivery cost. According to research, unexpected delivery cost accounts for 60% of abandoned carts. A shipping calculator that shows the cost upfront solves this and boosts conversion by 15–30%. Implementing such a calculator is non-trivial: you need to account for volumetric weight, integrate with multiple delivery service APIs (CDEK, Boxberry, Russian Post), ensure fault tolerance and calculation speed. API requests take 200–800 ms and are often paid, so proper caching and parallel processing are essential. We'll dive into technical details from storing rates in the database to parallel requests and error handling. We'll provide code examples in Laravel and React, discuss caching and working with volumetric weight. Implementing an aggregator pays off in 2–3 months, saving a significant amount on delivery.
Our team has 5+ years of experience in e-commerce development and has implemented 20+ shipping calculators. This allowed us to accumulate standard solutions that speed up integration. Reach out — we'll help choose the optimal architecture for your budget.
What the Calculator Computes
The delivery cost depends on parameters needed from various sources:
- Warehouse address (where items are shipped from) — can be fixed or the nearest store.
- Customer address (where to deliver) — to door or to pickup point.
- Weight and dimensions of items in the cart (what to deliver).
- Chosen delivery method — courier, pickup, parcel locker, Russian Post.
- Urgency — standard or express.
Item parameters are stored in the e-commerce database. Delivery rates are either in custom tables (for partner contracts with fixed prices) or come in real-time via the delivery service's API.
Local Tables vs. API: Two Approaches to Calculation
Local Rate Tables
For simple cases — when there is a fixed-price contract or self-delivery — rates are stored locally:
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,
];
});
}
}
Real-Time API Calculation: Example with CDEK
When up-to-date carrier rates are needed, we send a request to their 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();
}
}
Comparison of Approaches
| Criterion | Local Tables | Real-Time API |
|---|---|---|
| Calculation speed | <10 ms | 200–800 ms |
| Rate freshness | Manual update | Automatic |
| Integration complexity | Low | Medium |
| Maintenance cost | Low | Medium (API fees) |
Local tables are 10–80 times faster than API but less flexible. The choice depends on volume and freshness requirements.
How to Aggregate Multiple Delivery Services?
A real calculator typically shows options from several carriers simultaneously. Requests are made in parallel. If one service is unavailable, we show the rest. The customer never sees an error, just fewer options.
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();
}
Below is a comparison of popular delivery services for integration.
| Service | Delivery time (days) | Coverage | API Complexity | Fee |
|---|---|---|---|---|
| CDEK | 1–5 | Cities in Russia, CIS | Low | By rates |
| Boxberry | 2–7 | Cities in Russia, CIS | Medium | By rates |
| Russian Post | 3–14 | All regions of Russia | High | Low |
What Is Volumetric Weight and How Does It Affect Cost?
Many carriers charge the greater of actual and volumetric weight. According to CDEK rules, the billable weight is the maximum of actual and volumetric weight. Volumetric weight is a calculated weight based on dimensions. For air delivery, the divisor is 6000 cm³/kg; for sea, 1000 cm³/kg. If you ignore this, your rates will be underestimated. Example: a parcel weighing 2 kg with dimensions 50×40×30 cm gives a volumetric weight of 60000/5000=12 kg — you pay for 12 kg.
Optimizing Calculations with Caching
API requests to delivery services are slow (200–800 ms) and often paid. We cache by key consisting of origin city, destination city, and weight. Rates change rarely, so a 30-minute cache is optimal. When rates are updated, we invalidate the cache by pattern.
The Calculator Interface: Reactive Component
On the product page or cart, a compact block with a city input field and a list of methods with prices and delivery times, no page reload:
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="Enter your city"
/>
{loading && <Spinner />}
{options.map((opt) => (
<ShippingOption key={opt.id} option={opt} />
))}
</div>
);
};
A debounce of 600 ms prevents sending requests after every keystroke.
How Long Does Development Take?
- Analytics — gather requirements, define list of delivery services, rate zones.
- Design — choose stack (Laravel/PHP + React/Next.js), design database schema.
- Implementation — write the calculator, integrate APIs, develop UI component.
- Testing — check 100+ scenarios (different cities, weights, API errors).
- Deploy & monitoring — set up caching, alerts for failures.
Estimated timelines:
- Calculator with one service using fixed rates — 2–3 days.
- With real API of one service — 3–5 days (including error handling and caching).
- Aggregator for 3–5 services with an interface — 2–3 weeks.
Our clients save an average of 20–30% on delivery after implementing the aggregator, and the investment pays off in 2–3 months. For high-volume stores, the savings can be substantial. Contact us for a preliminary assessment of your project.
What's Included in the Result
- Documentation: rate scheme description, database schema, instructions for adding new services.
- Access to admin panel for managing rates.
- Staff training: how to modify rates, add zones.
- Technical support for one month after launch.
Get an engineer consultation: we'll help choose the optimal architecture and implement the calculator in 2–3 weeks.







