לקוח מוסיף פריטים לעגלה, עובר לתשלום, ורואה עלות משלוח גבוהה באופן בלתי צפוי. לפי מחקר, עלות משלוח בלתי צפויה אחראית ל-60% מעגלות נטושות. מחשבון משלוח שמציג את העלות מראש פותר זאת ומגביר המרות ב-15–30%. יישום מחשבון כזה אינו טריוויאלי: יש להתחשב במשקל נפחי, להתחבר לממשקי API של מספר חברות משלוחים (CDEK, Boxberry, דואר רוסיה), להבטיח סובלנות לתקלות ומהירות חישוב. בקשות API אורכות 200–800 אלפיות השנייה ולעיתים קרובות בתשלום, ולכן שמירה במטמון ועיבוד מקבילי הם חיוניים. נצלול לפרטים טכניים מאחסון תעריפים במסד הנתונים ועד בקשות מקבילות וטיפול בשגיאות. נספק דוגמאות קוד ב-Laravel וב-React, נדון בשמירה במטמון ובעבודה עם משקל נפחי. יישום אגרגטור מחזיר את ההשקעה תוך 2–3 חודשים, וחוסך סכום משמעותי על משלוח.
לצוות שלנו יש ניסיון של 5+ שנים בפיתוח מסחר אלקטרוני ויישמנו 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 בזמן אמת: דוגמה עם CDEK
כשנדרשים תעריפים עדכניים של המוביל, אנו שולחים בקשה ל-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) |
טבלאות מקומיות מהירות פי 10–80 מ-API אך פחות גמישות. הבחירה תלויה בנפח ובדרישות הרעננות.
איך לאגד מספר חברות משלוחים?
מחשבון אמיתי מציג בדרך כלל אפשרויות מכמה מובילים בו-זמנית. הבקשות נעשות במקביל. אם שירות אחד לא זמין, אנו מציגים את השאר. הלקוח אף פעם לא רואה שגיאה, רק פחות אפשרויות.
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 | עמלה |
|---|---|---|---|---|
| CDEK | 1–5 | ערים ברוסיה, חבר העמים | נמוכה | לפי תעריפים |
| Boxberry | 2–7 | ערים ברוסיה, חבר העמים | בינונית | לפי תעריפים |
| דואר רוסיה | 3–14 | כל אזורי רוסיה | גבוהה | נמוכה |
מה זה משקל נפחי ואיך הוא משפיע על העלות?
מובילים רבים גובים את הגדול מבין המשקל בפועל והמשקל הנפחי. לפי כללי CDEK, המשקל החייב הוא המקסימום של המשקל בפועל והמשקל הנפחי. משקל נפחי הוא משקל מחושב המבוסס על מידות. למשלוח אווירי, המחלק הוא 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>
);
};Debounce של 600 אלפיות השנייה מונע שליחת בקשות לאחר כל הקלדה.
כמה זמן לוקח הפיתוח?
- אנליטיקה — איסוף דרישות, הגדרת רשימת חברות משלוחים, אזורי תעריפים.
- עיצוב — בחירת מחסנית (Laravel/PHP + React/Next.js), עיצוב סכמת מסד נתונים.
- יישום — כתיבת המחשבון, אינטגרציית APIs, פיתוח רכיב UI.
- בדיקות — בדיקת 100+ תרחישים (ערים שונות, משקלים, שגיאות API).
- פריסה וניטור — הגדרת שמירה במטמון, התראות על תקלות.
לוחות זמנים משוערים:
- מחשבון עם שירות אחד באמצעות תעריפים קבועים — 2–3 ימים.
- עם API אמיתי של שירות אחד — 3–5 ימים (כולל טיפול בשגיאות ושמירה במטמון).
- אגרגטור ל-3–5 שירותים עם ממשק — 2–3 שבועות.
הלקוחות שלנו חוסכים בממוצע 20–30% על משלוח לאחר יישום האגרגטור, וההשקעה מחזירה את עצמה תוך 2–3 חודשים. לחנויות עם נפח גבוה, החיסכון יכול להיות משמעותי. צרו קשר להערכה ראשונית של הפרויקט שלכם.
מה כלול בתוצאה
- תיעוד: תיאור סכמת תעריפים, סכמת מסד נתונים, הוראות להוספת שירותים חדשים.
- גישה לפאנל ניהול לניהול תעריפים.
- הדרכת צוות: איך לשנות תעריפים, להוסיף אזורים.
- תמיכה טכנית לחודש אחד לאחר ההשקה.
קבלו ייעוץ מהנדס: נעזור לבחור ארכיטקטורה אופטימלית וליישם את המחשבון תוך 2–3 שבועות.







