Розробка інтернет-магазинів
Інтернет-магазин — це не «сайт із кошиком». Це система управління товарами, інвентарем, замовленнями, платежами, доставкою, поверненнями та комунікацією з клієнтами. Кожен із цих блоків має нетривіальну реалізацію, і більшість проблем у e-commerce виникає саме на стику цих систем.
Продуктивність каталогу: де все ламається
Найчастіша технічна проблема e-commerce — деградація продуктивності каталогу при зростанні SKU. Сторінка категорії працює добре на 500 товарах та починає гальмувати на 10 000. Причини майже завжди одні й ті ж.
N+1 на атрибутах. Завантажуєте список товарів — 50 елементів. Для кожного потрібні категорія, головне фото, ціна з урахуванням знижки, наявність на складі, рейтинг. Без правильного eager loading це 250+ запитів на сторінку. У Laravel вирішується через with(['category', 'mainImage', 'currentPrice', 'stockStatus']) та withAvg('reviews', 'rating'). Але стоїть додати персональні ціни (b2b) або складські остатки за регіонами — і один with() не рятує. Потрібен Query Object або виділений ReadModel.
Фасетна фільтрація без індексів. Фільтр за кольором + розміром + брендом + діапазоном цін на таблиці в 500K записів без складових індексів — це seq scan при кожному запиті. PostgreSQL з правильними індексами тримає фасетну фільтрацію до кількох мільйонів товарів. Для більших каталогів — Elasticsearch або OpenSearch з агрегаціями: вони рахують кількість товарів на фільтр (facet counts) значно швидше.
Пагінація через OFFSET. LIMIT 50 OFFSET 10000 на великій таблиці — погана ідея: PostgreSQL все рівно читає перші 10 050 рядків. Keyset pagination (cursor-based) через WHERE id > $last_id ORDER BY id LIMIT 50 працює за константне час незалежно від сторінки.
Конкретний кейс: каталог будівельних матеріалів, 180 000 SKU, фасетна фільтрація за 12 атрибутами. Після переходу з OFFSET-пагінації на cursor-based та додавання partial index за (category_id, is_active, price) — час відповіді сторінки каталогу знизився з 4.2s до 280ms.
Кошик та checkout
Checkout — це місце, де гроші либо потрапляють на рахунок, либо ні. Технічні проблеми тут коштують дорого.
Race condition при резервуванні товара. Два покупці одночасно додають останній екземпляр у кошик та обидва натискають «Оплатити». Без песимістичної блокування або атомарного UPDATE з перевіркою остатку — обидва замовлення проходять, інвентар йде в мінус. У PostgreSQL:
UPDATE inventory
SET reserved = reserved + $quantity
WHERE product_id = $id
AND (available - reserved) >= $quantity
RETURNING id;
Якщо RETURNING повернув 0 рядків — товара немає, показуємо помилку до списання грошей.
Ідемпотентність платіжних вебхуків. payment.succeeded від Stripe або ЮКассы може прийти двічі — мережні збої, retry logic на стороні платіжного шлюзу. Без перевірки WHERE NOT EXISTS (SELECT 1 FROM processed_events WHERE event_id = $id) — дублювання замовлення або подвійне зачислення.
Checkout у кілька кроків. Multi-step checkout (адреса → доставка → оплата → підтвердження) vs single-page checkout. Дослідження показують, що single-page з прогрес-індикатором конвертує краще на мобільних. Стан між кроками — либо localStorage + server-side сесія, либо повністю server-side з проміжним збереженням.
Інтеграції: 1С, склад, доставка
1С — окремий розділ. Три поширених способи інтеграції:
- CommerceML через HTTP — 1С виносить XML за розкладом, сайт імпортує. Працює для невеликих каталогів, є затримка синхронізації.
- REST API / OData від 1С — двостороння синхронізація в реальному часі. Вимагає налаштування на стороні 1С, примхлива до версій конфігурацій.
- Проміжна шина (RabbitMQ / Kafka) — 1С публікує події, сайт підписується. Найнадійніший підхід для високонавантажених систем, але найдорожчий у розробці.
Типова біль CommerceML: файл виносу 200MB з повним каталогом кожні 30 хвилин. Парсинг блокує чергу, імпорт займає 10–15 хвилин, у це час на сайті старі ціни. Рішення — інкрементальна виносу (тільки зміни) та фонова обробка через Laravel Queue із кількома workers.
Служби доставки — СДЕК, Boxberry, Поща Росії, DHL: всі надають REST API для розрахунку вартості та створення накладних. Агрегатори (Shiptor, Shipnow) дозволяють працювати з кількома службами через єдиний API.
Платіжні шлюзи
| Шлюз | Особливості інтеграції |
|---|---|
| Stripe | Webhook-based, чудова документація, Stripe Elements для PCI DSS |
| ЮКасса | Популярна в РФ, підтримка ФЗ-54 (фіскалізація) |
| ЕРIP | Білоруська система, SOAP API, специфічна документація |
| Tinkoff Acquiring | REST API, 3D Secure 2.0, webhook-сповіщення |
Для кожного шлюзу обов'язкова перевірка підпису вебхука — без цього будь-хто може відправити фейкове payment.succeeded.
CMS проти власної розробки
WooCommerce — виправданий для магазинів до ~5 000 SKU із типовою бізнес-логікою. Швидкий старт, величезна екосистема плагінів. Проблеми починаються з нестандартних цінових правил, складних варіантів товарів або навантаження від 10 000+ замовлень на місяць.
OpenCart, Prestashop — аналогічна історія. Добре для старту, обмежені при зростанні.
Власна розробка на Laravel — для:
- Нестандартної бізнес-логіки (підписки, оренда, b2b-прайс-листи, конфігуратор)
- Високих вимог до продуктивності
- Складних інтеграцій (кілька складів, ERP, маркетплейси)
- Унікального UX checkout
SEO для e-commerce
Canonical та дублювання. Фасетна фільтрація генерує тисячі URL (?color=red&size=M&sort=price). Без canonical або noindex на фільтрованих сторінках — краулинговий бюджет розходується на дубліки, а основні сторінки індексуються гірше.
Structured data. Product schema з offers, aggregateRating, availability — це rich snippets у виданні: зірочки рейтингу, ціна, наявність. Впливає на CTR.
Core Web Vitals на сторінках товарів. Hero image товара — це LCP element. fetchpriority="high" на першому зображенні, правильні srcset з WebP, width та height атрибути для запобігання CLS.
Орієнтири за термінами
| Тип магазину | Термін |
|---|---|
| Малий (до 1 000 SKU, типова логіка) | 8–12 тижнів |
| Середній (до 50 000 SKU, інтеграція 1С) | 14–20 тижнів |
| Крупний (100 000+ SKU, ERP, маркетплейси) | 24–40 тижнів |
Вартість розраховується після аналізу вимог: кількість інтеграцій, складність ціноутворення, обсяг каталогу та унікальність UX — основні фактори.
Чек-лист перед запуском
- Race condition при оплаті останнього товара — покритий тестом
- Ідемпотентність вебхуків платіжного шлюзу
- Rate limiting на ендпоїнтах кошика та checkout
- Canonical на фільтрованих сторінках каталогу
- Фіскалізація чеків (ФЗ-54 для РФ або аналог)
- Стрес-тест checkout під навантаженням (k6 або Locust)
- Моніторинг помилок (Sentry) та алерти на payment errors
- Backup бази даних з перевіреним restore-процесом







