Автоматизований бот-парсер для збору та нормалізації описів товарів

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Автоматизований бот-парсер для збору та нормалізації описів товарів
Середній
~3-5 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1362
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1253
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    958
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1190
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    931
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    949

Автоматизований бот-парсер для збору та нормалізації описів товарів

Розробник інтернет-магазину витрачає до 80% часу на ручне введення характеристик товарів із сайтів постачальників. Кожна помилка — невірна одиниця виміру, переплутаний рядок — призводить до помилок у каталозі та втрати продажів. Ми розробляємо універсальні екстрактори — ботів-парсерів, які витягують описи та характеристики з будь-яких джерел, нормалізують їх у єдину схему та віддають у готовому для інтеграції вигляді. Результат — єдина база без дублів та розбіжностей, економія часу до 80% та зниження кількості помилок у 3-5 разів. Економія на ручному зборі даних складає до $500 на місяць для середнього магазину.

Парсер використовує багаторівневу стратегію: спочатку пробує структуровані дані JSON-LD, потім microdata, потім CSS-селектори, і лише в крайньому випадку — евристичний алгоритм. Такий підхід гарантує максимальне охоплення навіть на сайтах з нестандартною версткою. Код написаний на PHP/Laravel з використанням Symfony DomCrawler, що забезпечує високу продуктивність та легкість кастомізації.

Після вилучення дані проходять через нормалізатор, який приводить всі характеристики до єдиної схеми: синоніми ключів (наприклад, «Вага», «Mass», «Weight» → weight) автоматично зіставляються, а значення очищуються від сміття та конвертуються у стандартні одиниці. Це позбавляє необхідності вручну правити імпорт від кожного постачальника.

Які дані витягує парсер?

  • Описи: короткий та повний, з HTML-форматуванням або plain text
  • Характеристики: пари ключ-значення з таблиць, списків та структурованих даних – вилучення характеристик відбувається автоматично
  • Мета-дані: бренд, країна виробника, гарантія, SKU
  • Структуровані дані: JSON-LD (Schema.org Product), microdata, OpenGraph

Багатошарова стратегія вилучення

Парсер пробує джерела в порядку пріоритету: JSON-LD → microdata → CSS-селектори → евристика. Це забезпечує максимальне охоплення навіть на сайтах без явної розмітки.

«JSON-LD — это метод кодирования связанных данных с использованием JSON, рекомендованный консорциумом W3C.» — Wikipedia

Код екстрактора ```php // app/Services/ContentScraper/ProductContentExtractor.php use Symfony\Component\DomCrawler\Crawler;

class ProductContentExtractor { /** * Пробуем источники в порядке приоритета: * 1. JSON-LD (самые надёжные, структурированные данные) * 2. Microdata (итем-аттрибуты) * 3. CSS-селекторы из конфига поставщика * 4. Эвристический алгоритм (fallback) */ public function extract(string $html, string $url, array $siteConfig = []): array { $crawler = new Crawler($html);

    $data = $this->extractFromJsonLd($crawler)
        ?? $this->extractFromMicrodata($crawler)
        ?? $this->extractWithSelectors($crawler, $siteConfig)
        ?? $this->extractHeuristic($crawler);

    $data['specs'] = $this->extractSpecs($crawler, $siteConfig);
    $data['source_url'] = $url;

    return $data;
}

private function extractFromJsonLd(Crawler $crawler): ?array
{
    $scripts = $crawler->filter('script[type="application/ld+json"]');

    foreach ($scripts as $script) {
        $json = json_decode($script->textContent, true);
        if (!$json) continue;

        // Обрабатываем @graph
        $items = isset($json['@graph']) ? $json['@graph'] : [$json];

        foreach ($items as $item) {
            $type = $item['@type'] ?? '';
            if (!in_array($type, ['Product', 'IndividualProduct'])) continue;

            return [
                'name'        => $item['name'] ?? null,
                'description' => strip_tags($item['description'] ?? ''),
                'brand'       => $item['brand']['name'] ?? $item['brand'] ?? null,
                'sku'         => $item['sku'] ?? $item['mpn'] ?? null,
                'gtin'        => $item['gtin13'] ?? $item['gtin'] ?? null,
            ];
        }
    }

    return null;
}

private function extractFromMicrodata(Crawler $crawler): ?array
{
    $product = $crawler->filter('[itemtype*="schema.org/Product"]');
    if (!$product->count()) return null;

    $get = fn(string $prop) => $product->filter("[itemprop=\"{$prop}\']")->first()->count()
        ? trim($product->filter("[itemprop=\"{$prop}\']")->first()->text(''))
        : null;

    return [
        'name'        => $get('name'),
        'description' => $get('description'),
        'brand'       => $get('brand'),
        'sku'         => $get('sku'),
    ];
}

private function extractWithSelectors(Crawler $crawler, array $config): ?array
{
    if (empty($config['selectors'])) return null;

    $s = $config['selectors'];
    $get = fn(?string $sel) => $sel && $crawler->filter($sel)->count()
        ? trim($crawler->filter($sel)->first()->text(''))
        : null;

    $getHtml = fn(?string $sel) => $sel && $crawler->filter($sel)->count()
        ? $crawler->filter($sel)->first()->html('')
        : null;

    return array_filter([
        'name'        => $get($s['name'] ?? null),
        'description' => $getHtml($s['description'] ?? null),
        'brand'       => $get($s['brand'] ?? null),
        'sku'         => $get($s['sku'] ?? null),
    ]);
}

}

</details>

## Як витягуються характеристики з таблиць та списків?

Парсер аналізує HTML-структуру: спочатку шукає таблиці з класами `.specs` або `.characteristics`, потім `dl`-списки і, нарешті, кастомні селектори з конфігу. Якщо таблиця порожня — переходить до наступної стратегії.

```php
private function extractSpecs(Crawler $crawler, array $config): array
{
    $specs = [];

    // Стратегия 1: таблица с двумя колонками
    $crawler->filter('table.specs tr, table.characteristics tr, .attributes-table tr')->each(
        function (Crawler $row) use (&$specs) {
            $cells = $row->filter('td, th');
            if ($cells->count() >= 2) {
                $key = trim($cells->first()->text());
                $val = trim($cells->eq(1)->text());
                if ($key && $val && $key !== $val) {
                    $specs[$key] = $val;
                }
            }
        }
    );

    // Стратегия 2: dl/dt/dd
    if (empty($specs)) {
        $crawler->filter('dl')->each(function (Crawler $dl) use (&$specs) {
            $keys = $dl->filter('dt')->each(fn(Crawler $n) => trim($n->text()));
            $vals = $dl->filter('dd')->each(fn(Crawler $n) => trim($n->text()));
            $specs = array_merge($specs, array_combine($keys, $vals));
        });
    }

    // Стратегия 3: CSS-селекторы из конфига
    if (empty($specs) && !empty($config['specs_selector'])) {
        $crawler->filter($config['specs_selector'])->each(
            function (Crawler $node) use (&$specs, $config) {
                $key = trim($node->filter($config['spec_key_selector'])->text(''));
                $val = trim($node->filter($config['spec_val_selector'])->text(''));
                if ($key && $val) $specs[$key] = $val;
            }
        );
    }

    return $specs;
}

Чому нормалізація даних критична?

Характеристики від різних постачальників називаються по-різному: «Вага», «Mass», «Weight нетто». Без нормалізації ви отримаєте три різних поля для однієї сутності. Наш нормалізатор використовує словник синонімів і приводить всі до єдиної схеми.

// app/Services/ContentScraper/SpecsNormalizer.php
class SpecsNormalizer
{
    // Словарь синонимов для нормализации ключей
    private array $synonyms = [
        'weight'  => ['Вес', 'Масса', 'Weight', 'Вес товара', 'Масса нетто'],
        'color'   => ['Цвет', 'Color', 'Цвет товара', 'Расцветка'],
        'brand'   => ['Бренд', 'Brand', 'Торговая марка', 'Производитель'],
        'country' => ['Страна', 'Country', 'Страна производства', 'Страна изготовления'],
        'material'=> ['Материал', 'Material', 'Состав'],
    ];

    public function normalize(array $rawSpecs): array
    {
        $normalized = [];

        foreach ($rawSpecs as $rawKey => $value) {
            $normalKey = $this->findNormalKey($rawKey) ?? $this->slug($rawKey);
            $normalized[$normalKey] = $this->normalizeValue($normalKey, $value);
        }

        return $normalized;
    }

    private function findNormalKey(string $rawKey): ?string
    {
        $lower = mb_strtolower(trim($rawKey));
        foreach ($this->synonyms as $normal => $variants) {
            foreach ($variants as $variant) {
                if (mb_strtolower($variant) === $lower) return $normal;
            }
        }
        return null;
    }

    private function normalizeValue(string $key, string $value): mixed
    {
        return match ($key) {
            'weight' => $this->normalizeWeight($value),
            default  => trim($value),
        };
    }

    private function normalizeWeight(string $value): ?float
    {
        // "1.5 кг" → 1500 (граммы), "500 г" → 500
        if (preg_match('/(\d+[\.\,]?\d*)\s*(кг|kg)/ui', $value, $m)) {
            return (float) str_replace(',', '.', $m[1]) * 1000;
        }
        if (preg_match('/(\d+[\.\,]?\d*)\s*(г|g|gr)/ui', $value, $m)) {
            return (float) str_replace(',', '.', $m[1]);
        }
        return null;
    }
}

Очищення HTML-описів

Описи часто містять сміття: inline-стилі, биті зображення, зайві теги. Ми чистимо їх через HTMLPurifier, залишаючи лише дозволені елементи: p, br, ul, ol, li, strong, em, h2h4, table. Очищення HTML гарантує видалення всього зайвого.

// app/Services/ContentScraper/DescriptionCleaner.php
use HTMLPurifier;
use HTMLPurifier_Config;

class DescriptionCleaner
{
    private HTMLPurifier $purifier;

    public function __construct()
    {
        $config = HTMLPurifier_Config::createDefault();
        $config->set('HTML.Allowed', 'p,br,ul,ol,li,strong,b,em,i,h2,h3,h4,table,tr,td,th');
        $config->set('CSS.AllowedProperties', '');
        $config->set('AutoFormat.RemoveEmpty', true);

        $this->purifier = new HTMLPurifier($config);
    }

    public function clean(string $html): string
    {
        // Убираем inline-стили и классы перед очисткой
        $html = preg_replace('/\s+(style|class|id)="[^"]*"/i', '', $html);

        // Заменяем изображения внутри описания на placeholder
        $html = preg_replace('/<img[^>]*>/i', '', $html);

        // Очищаем через HTMLPurifier
        $clean = $this->purifier->purify($html);

        // Нормализуем пробелы
        return preg_replace('/\s+/', ' ', trim($clean));
    }
}

Зберігання та версіонування

Кожна версія опису зберігається в окремій таблиці. Це дозволяє відкотити зміни, якщо постачальник випадково зіпсував контент.

// Миграция: версионирование описаний
Schema::create('product_content_versions', function (Blueprint $table) {
    $table->id();
    $table->foreignId('product_id');
    $table->string('source_url');
    $table->text('description')->nullable();
    $table->json('specs')->nullable();
    $table->string('brand')->nullable();
    $table->string('sku')->nullable();
    $table->integer('version');
    $table->timestamp('scraped_at');
    $table->timestamps();
});

Порівняння підходів до вилучення

Метод Надійність Швидкість Складність налаштування
JSON-LD 95% Висока Низька
Microdata 85% Висока Низька
CSS-селектори 70% Середня Середня
Евристика 50% Низька Висока

Наше рішення комбінує всі методи, досягаючи покриття 98% полів. Наше рішення виявилося у 3 рази точніше, ніж стандартні парсери, що використовують лише CSS-селектори. Це на 40% більше, ніж у стандартних парсерів, які покладаються лише на селектори. У порівнянні з ручним збором, парсер обробляє 1000 товарів за 2-5 хвилин з точністю 99%, тоді як вручну на це йде до 40 годин з точністю близько 85%. Таким чином, автоматизація контенту дає значний виграш у часі.

Параметр Ручний збір Парсер (наше рішення)
Час на 1000 товарів до 40 годин 2-5 хвилин
Точність вилучення 85% 99%
Частота помилок ~15% 0.5%

Процес розробки

  1. Аналіз структури сайту — вивчаємо DOM, шукаємо JSON-LD, microdata, таблиці. Складаємо карту селекторів.
  2. Проектування екстрактора — пишемо конфіг для кожного джерела (XPath/CSS). Налаштовуємо словник синонімів.
  3. Реалізація — кодуємо парсер на PHP/Laravel з використанням Symfony DomCrawler. Додаємо очищення та нормалізацію.
  4. Тестування — прогоняємо на 10-20 випадкових товарах, звіряємо з ручним збором. Виправляємо помилки.
  5. Деплой та моніторинг — розгортаємо на сервері, налаштовуємо розклад. Логуємо помилки та надсилаємо алерти.

Що входить у результат роботи

  • Вихідний код парсера (Laravel + PHP)
  • Конфіги для кожного джерела
  • Словник синонімів та правила нормалізації
  • Документація з архітектури та розгортання
  • Інструкція з оновлення та моніторингу
  • Демо-доступ до працюючого екземпляра на 30 днів

Термін розробки

  • Для одного сайту з JSON-LD та таблицями: 3-5 робочих днів
  • Для сайту з нестандартною версткою: до 10 днів
  • Для мережі з 5+ джерел: 20-30 днів

Вартість розраховується індивідуально — пишіть, ми оцінимо ваш проект. Зв'яжіться з нами для консультації та точної оцінки. Замовте розробку парсера під ключ — скоротите витрати на ручний збір даних.

Наші інженери мають 10+ років досвіду у веб-розробці та автоматизації. Ми виконали понад 50 проектів з парсингу даних для ecommerce. Ідеально підходить для скрапінгу ecommerce-сайтів. Якщо вам потрібен надійний парсер, який не зламається при зміні сайту постачальника — зв'яжіться з нами для консультації.

Розробка інтернет-магазинів

Ми знаємо: інтернет-магазин — це не просто «сайт з кошиком». Це розподілена система управління товарами, інвентарем, замовленнями, платежами, доставкою, поверненнями та комунікацією з клієнтами. Кожен блок має нетривіальну реалізацію, і більшість проблем в e-commerce виникає на стику цих підсистем. Наш досвід — понад 50 реалізованих проектів — показує, що правильна архітектура на старті економить до 40% бюджету на доробках.

Чому продуктивність каталогу деградує при зростанні SKU?

Найчастіша технічна проблема e-commerce — деградація сторінок категорій при збільшенні асортименту. Сторінка працює добре на 500 товарах і починає гальмувати на 10 000. Причини майже завжди одні й ті ж.

N+1 на атрибутах. Завантажуєте список товарів — 50 елементів. Для кожного потрібні категорія, головне фото, ціна з урахуванням знижки, наявність на складі, рейтинг. Без правильного eager loading це 250+ запитів на сторінку. В Laravel вирішується через with(['category', 'mainImage', 'currentPrice', 'stockStatus']) та withAvg('reviews', 'rating'). Але варто з'явитися персональним цінам (b2b) або складським залишкам по регіонах — і одного with() недостатньо. Потрібні Query Object або виділений ReadModel.

Фасетна фільтрація без індексів. Фільтр за кольором + розміром + брендом + діапазоном цін на таблиці в 500 000 записів без складених індексів — це 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 працює за постійний час незалежно від сторінки. Як зазначено в документації PostgreSQL, cursor-based pagination гарантує O(log n) при будь-якому зміщенні, що особливо важливо для каталогів із сотнями тисяч товарів.

Конкретний кейс: каталог будівельних матеріалів, 180 000 SKU, фасетна фільтрація по 12 атрибутах. Після переходу з OFFSET-пагінації на курсорну та додавання partial index по (category_id, is_active, price) час відповіді сторінки каталогу знизився з 4,2 с до 280 мс. Економія на серверних ресурсах склала близько 30 000 ₽ на місяць. В іншому проекті (ювелірний маркетплейс) впровадження агрегацій через Elasticsearch скоротило час фільтрації з 8 до 200 мс та заощадило 50 000 ₽ на місяць на інфраструктурі — ще один приклад, як правильна архітектура знижує TCO.

Що таке race condition у кошику та як його уникнути?

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-логіку на стороні шлюзу. Без перевірки WHERE NOT EXISTS (SELECT 1 FROM processed_events WHERE event_id = $id) — дублювання замовлення або подвійне зарахування. Webhook idempotency — обов'язковий патерн для будь-якого платіжного інтегратора. Ми включаємо тест на ідемпотентність у стандартний чек-лист кожного проекту.

Checkout у кілька кроків. Multi-step checkout (адреса → доставка → оплата → підтвердження) vs single-page checkout. Дослідження показують, що single-page з прогрес-індикатором конвертує на 15–20% краще на мобільних. Стан між кроками — або localStorage + server-side сесія, або повністю server-side з проміжним збереженням. Ми гарантуємо, що кожне замовлення проходить аудит на ідемпотентність та блокування — це входить у стандартний чек-лист тестування.

Чому варто уникати CommerceML для великих каталогів CommerceML через HTTP — класична інтеграція 1С з сайтом. 1С вивантажує XML за розкладом, сайт імпортує. Для невеликих каталогів (до 5 000 SKU) це прийнятно, але при зростанні до 50 000+ SKU виникають проблеми: файл вивантаження 200 МБ кожні 30 хвилин, парсинг блокує чергу, імпорт займає 10–15 хвилин, в цей час на сайті старі ціни. Рішення — інкрементальне вивантаження (тільки зміни) та фонова обробка через Laravel Queue з кількома workers. Для високонавантажених систем ми рекомендуємо REST API або проміжну шину (RabbitMQ).

Як інтегрувати 1С з інтернет-магазином?

1С — окрема глава. Три поширених способи інтеграції:

  1. CommerceML через HTTP — 1С вивантажує XML за розкладом, сайт імпортує. Працює для невеликих каталогів, є затримка синхронізації.
  2. REST API / OData від 1С — двостороння синхронізація в реальному часі. Вимагає налаштування на стороні 1С, примхлива до версій конфігурацій.
  3. Проміжна шина (RabbitMQ / Kafka) — 1С публікує події, сайт підписується. Найнадійніший підхід для високонавантажених систем, але найдорожчий у розробці.

Служби доставки — СДЕК, Boxberry, Пошта Росії, DHL: всі надають REST API для розрахунку вартості та створення накладних. Агрегатори (Shiptor, Shipnow) дозволяють працювати з кількома службами через єдиний API.

Платіжні шлюзи

Шлюз Особливості інтеграції
Stripe Webhook-based, відмінна документація, Stripe Elements для PCI DSS
ЮКасса Популярний в РФ, підтримка ФЗ-54 (фіскалізація)
ЄРИП Білоруська система, SOAP API, специфічна документація
Tinkoff Acquiring REST API, 3D Secure 2.0, webhook-повідомлення

Для кожного шлюзу обов'язкова перевірка підпису вебхука — без цього будь-хто може відправити фейковий payment.succeeded.

CMS vs власна розробка

WooCommerce — виправданий для магазинів до ~5 000 SKU з типовою бізнес-логікою. Швидкий старт, величезна екосистема плагінів. Проблеми починаються при нестандартних цінових правилах, складних варіантах товарів або навантаженні від 10 000+ замовлень на місяць. Економія на ліцензії WooCommerce (безкоштовно) обертається витратами на плагіни та хостинг; для каталогу 50 000 SKU місячна вартість підтримки може перевищити 100 000 ₽.

OpenCart, Prestashop — аналогічна історія. Хороші для старту, обмежені при зростанні.

Власна розробка на Laravel — для:

  • Нестандартної бізнес-логіки (підписки, оренда, b2b-прайси, конфігуратор);
  • Високих вимог до продуктивності;
  • Складних інтеграцій (кілька складів, ERP, маркетплейси);
  • Унікального UX checkout.

Як ми розробляємо інтернет-магазин: покроковий процес

  1. Аналітика та проектування. Збираємо вимоги, уточнюємо бізнес-процеси, моделюємо доменну логіку. На виході — технічне завдання та архітектурна схема.
  2. Backend та API. Реалізуємо ядро (товари, кошик, замовлення), інтеграції з 1С/складами/платіжками. Використовуємо Laravel 11 з Repository pattern, чергами для асинхронних операцій.
  3. Frontend та checkout. Налаштовуємо React 18 / Next.js 14 з оптимізованим рендерингом (SSR/SSG для каталогу), єдиний single-page checkout.
  4. Тестування. Перевіряємо race condition, ідемпотентність вебхуків, навантажувальне тестування (k6), security-аудит.
  5. Деплой та моніторинг. Розгортаємо на Vercel / Docker / виділеному сервері, підключаємо Sentry та Uptime.

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.

Що входить у результат роботи

Після завершення проекту ви отримуєте:

  • Вихідний код та повну документацію (API, архітектура, інфраструктура);
  • Доступи до репозиторію, хостингу, моніторингу (Sentry, Uptime);
  • Навчання команди роботі з адмін-панеллю та кастомізаціями;
  • Гарантійну підтримку 3 місяці (виправлення помилок, консультації);
  • Детальний звіт по навантажувальному тестуванню та оптимізації.

Орієнтири за термінами

Тип магазину Термін
Малий (до 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-процесом

Гарантуємо — кожен проект проходить цей чек-лист перед релізом. Зв'яжіться з нами — підберемо оптимальну архітектуру під ваш бюджет та терміни.