Розробка карти вибору пункту видачі для інтернет-магазину
Покупець відкриває крок доставки, бачить список з 47 адрес пунктів видачі, намагається знайти потрібний район у випадаючому списку та закриває вкладку. Наш інженерний досвід показує: без інтерактивної карти до 40% користувачів кидають замовлення на цьому етапі. Ми – команда з 7-річним досвідом у розробці e-commerce рішень, реалізували понад 30 подібних проектів. З нашим рішенням людина одразу бачить точки на карті біля дому, роботи, по дорозі – і обирає за 5 секунд. Ми впровадили такі карти для 30+ інтернет-магазинів, середнє зростання конверсії на етапі вибору доставки – 18%.
Проблеми вибору ПВЗ та як карта їх вирішує
Google Analytics показує, що сторінка вибору доставки – одна з найбільш «просадних»: до 40% користувачів йдуть, не вибравши спосіб. Без карти покупець змушений прокручувати довгий список адрес, зіставляти з уявною картою міста і часто помиляється або кидає оформлення. Типові складності:
- Довгі списки без візуальної прив'язки – назви вулиць ні про що не говорять, якщо людина погано знає район.
- Відсутність інформації про завантаженість – ПВЗ може бути переповнений, але у списку цього не видно.
- Неможливість порівняти кілька точок одночасно – доводиться запам'ятовувати адреси та перемикатися між вкладками.
Карта повертає наочність: маркери показують розташування, спливаючі підказки – деталі (режим роботи, терміни, вартість), групування в кластери – масштабованість. В результаті менше покинутих кошиків та економія бюджету на ретаргетингу. Економія на логістиці досягає 20-30% за рахунок зниження повернень.
Джерела даних про ПВЗ
Пункти видачі надходять з API служб доставки. У кожної своя структура, але суть одна – список об'єктів з координатами, адресою, годинами роботи, обмеженнями за вагою та габаритами.
СДЕК:
GET https://api.cdek.ru/v2/deliverypoints?city_code=44&weight_max=30&type=PVZ Authorization: Bearer {token} Boxberry:
GET https://api.boxberry.ru/json.php?token={token}&method=ListPoints&CityCode=77&prepaid=1 Як ми кешуємо довідник ПВЗ?
Список ПВЗ змінюється рідко – раз на добу. Запитувати його при кожному відкритті сторінки – марнотратство. Ми синхронізуємо дані за розкладом: щоночі artisan-команда завантажує всі точки від кожного перевізника, очищає таблицю і вставляє актуальний набір. Користувач отримує дані з локальної БД за 10–20 мс замість 500–2000 мс від API.
Приклад artisan-команди для синхронізації
class SyncPickupPoints extends Command { public function handle(CdekService $cdek, BoxberryService $boxberry): void { $carriers = [ 'cdek' => fn() => $cdek->getAllPickupPoints(), 'boxberry' => fn() => $boxberry->getAllPickupPoints(), ]; foreach ($carriers as $carrier => $fetcher) { $points = $fetcher(); $this->info("$carrier: {$points->count()} points"); PickupPoint::where('carrier', $carrier)->delete(); PickupPoint::insert( $points->map(fn($p) => [ 'carrier' => $carrier, 'external_id' => $p['code'], 'name' => $p['name'], 'address' => $p['address'], 'city' => $p['city'], 'lat' => $p['lat'], 'lng' => $p['lng'], 'work_time' => $p['work_time'], 'max_weight' => $p['max_weight_kg'], 'cash_allowed' => $p['cash_allowed'], 'updated_at' => now(), ])->toArray() ); } $this->info('Done'); } } Як ми оптимізуємо геопошук?
Для пошуку найближчих ПВЗ використовуємо PostGIS. Порівняйте два підходи: PostGIS з просторовим індексом GIST проти формули гаверсинуса на стороні SQL. Виграш у швидкості – в 10-30 разів на наборах понад 10 000 точок.
| Критерій | PostGIS (GIST) | Формула гаверсинуса в SQL |
|---|---|---|
| Час виконання запиту (10 км, 20 точок) | 5–15 мс | 50–200 мс |
| Точність | Відстань по сфероїду | Приблизна |
| Підтримка радіуса | Вбудована (ST_DWithin) | Розрахунок в WHERE |
| Індексування | Просторовий індекс GIST | Немає, повне сканування |
Ми обираємо PostGIS: він дає виграш у швидкості в 10–30 разів на великих наборах даних. Документація PostGIS стверджує, що просторові індекси роблять запити до геоданих на порядок швидше.
CREATE EXTENSION IF NOT EXISTS postgis; ALTER TABLE pickup_points ADD COLUMN location geography(POINT, 4326); UPDATE pickup_points SET location = ST_SetSRID(ST_MakePoint(lng, lat), 4326); CREATE INDEX idx_pickup_points_location ON pickup_points USING GIST(location); SELECT id, carrier, name, address, work_time, cash_allowed, ST_Distance(location, ST_SetSRID(ST_MakePoint(:lng, :lat), 4326)) AS distance_m FROM pickup_points WHERE max_weight >= :weight AND ST_DWithin(location, ST_SetSRID(ST_MakePoint(:lng, :lat), 4326)::geography, 10000) ORDER BY distance_m LIMIT 20; Рендеринг карти з кластеризацією
Зазначимо: коли точок багато – кілька тисяч – рендерити кожну як окремий DOM-елемент не можна, браузер зависне. Використовуємо Leaflet + MarkerClusterGroup. Завантаження даних відбувається тільки для видимої області карти (подія moveend).
import L from 'leaflet'; import 'leaflet.markercluster'; const map = L.map('pickup-map').setView([55.7558, 37.6173], 11); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); const markers = L.markerClusterGroup({ maxClusterRadius: 50, iconCreateFunction: (cluster) => { const count = cluster.getChildCount(); return L.divIcon({ html: `<div class="cluster-icon">${count}</div>`, className: '', iconSize: [40, 40], }); }, }); map.on('moveend', async () => { const bounds = map.getBounds(); const response = await fetch('/api/pickup-points?' + new URLSearchParams({ north: bounds.getNorth(), south: bounds.getSouth(), east: bounds.getEast(), west: bounds.getWest(), weight: cartWeight, })); const points = await response.json(); markers.clearLayers(); points.forEach((point) => { const marker = L.marker([point.lat, point.lng], { icon: carrierIcon(point.carrier), }); marker.bindPopup(buildPopup(point)); marker.on('click', () => selectPickupPoint(point)); markers.addLayer(marker); }); }); map.addLayer(markers); Що входить у роботу
При замовленні розробки ви отримуєте:
- Інтеграцію з 1–5 службами доставки (API, синхронізація, кешування)
- Геопошук через PostGIS або альтернативний алгоритм
- Карту з кластеризацією, фільтрами та попапами
- Адаптивну верстку під мобільні пристрої
- Визначення міста покупця (геолокація + IP)
- Документацію по API та інструкцію з оновлення довідника
- 2 тижні безкоштовної підтримки після запуску
- Гарантію на розроблене рішення – 6 місяців
Процес роботи
- Аналітика – вивчаємо вашу форму замовлення, список служб доставки, типові сценарії покупців.
- Проєктування – погоджуємо структуру даних, API-методи, логіку фільтрів.
- Реалізація – пишемо код, налаштовуємо cron-завдання, підключаємо карту.
- Тестування – перевіряємо на реальних даних, імітуємо 1000+ точок.
- Деплой та навчання – вивантажуємо на бойовий сервер, передаємо доступи та документацію.
Терміни розробки
| Етап | Термін |
|---|---|
| Одна служба доставки, карта з кластеризацією | 4–6 днів |
| Агрегатор 3–5 служб, фільтри, геолокація | 2 тижні |
| Додавання карти в існуючу форму замовлення | 3–5 днів |
Мінімальна вартість робіт – від 15 000 грн за інтеграцію однієї служби доставки. Остаточна вартість розраховується індивідуально – залежить від кількості служб, складності фільтрів та необхідності переробки форми замовлення. Грамотна карта ПВЗ окупається за рахунок зростання конверсії – вкладення повертаються протягом 3–6 місяців.
Зв'яжіться з нами, щоб обговорити ваш проєкт та отримати демо працюючої карти. Замовте карту ПВЗ під ключ – оцінимо ваш проект безкоштовно.
Детальніше про PostGIS та Leaflet.markercluster.







