Разработка системы групповых покупок на 1С-Битрикс

Разработка системы групповых покупок на 1С-Битрикс — задача, с которой к нам обращаются, когда стандартные скидки не работают. Коробочный Битрикс не умеет привязывать скидку к числу участников: модуль `sale` оперирует индивидуальными заказами, а `b_catalog_discount` статичен. Мы строим всю логику по
Услуги, которые мы предлагаем
Показано 1 из 1Все 1626 услуг
Разработка системы групповых покупок на 1С-Битрикс
Средний
~1-2 недели

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

Часто задаваемые вопросы

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1460
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1019
  • Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    764
  • Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    882
  • Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    810
  • Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    1166

Разработка системы групповых покупок на 1С-Битрикс — задача, с которой к нам обращаются, когда стандартные скидки не работают. Коробочный Битрикс не умеет привязывать скидку к числу участников: модуль sale оперирует индивидуальными заказами, а b_catalog_discount статичен. Мы строим всю логику поверх стандартной архитектуры, используя кастомные таблицы и транзакции. Типичный кейс: интернет-магазин запускает акцию «чем больше покупателей, тем ниже цена». Без доработки каждый новый участник оформляет заказ по одной цене. Мы добавляем систему, которая динамически пересчитывает стоимость и синхронизирует счётчик участников в реальном времени. Наша команда имеет 10+ лет опыта в разработке на 1С-Битрикс и десятки успешных проектов. Мы знаем все подводные камни и готовы реализовать систему под ключ за 1–5 недель.

Система групповых покупок

Система строится вокруг сущности «акция групповой покупки». Удобнее всего хранить её в отдельном HL-блоке или кастомной таблице. Пример структуры:

CREATE TABLE b_group_deal ( ID INT AUTO_INCREMENT PRIMARY KEY, PRODUCT_ID INT NOT NULL, DATE_START DATETIME NOT NULL, DATE_END DATETIME NOT NULL, MIN_PARTICIPANTS INT NOT NULL, MAX_PARTICIPANTS INT, CURRENT_COUNT INT DEFAULT 0, STATUS ENUM('active','success','failed','closed') DEFAULT 'active', INDEX idx_product (PRODUCT_ID), INDEX idx_status_date (STATUS, DATE_END) ); CREATE TABLE b_group_deal_tier ( ID INT AUTO_INCREMENT PRIMARY KEY, DEAL_ID INT NOT NULL, PARTICIPANTS_FROM INT NOT NULL, DISCOUNT_PERCENT DECIMAL(5,2), PRICE_FIXED DECIMAL(10,2), FOREIGN KEY (DEAL_ID) REFERENCES b_group_deal(ID) ); CREATE TABLE b_group_deal_participant ( ID INT AUTO_INCREMENT PRIMARY KEY, DEAL_ID INT NOT NULL, USER_ID INT NOT NULL, ORDER_ID INT, DATE_ADD DATETIME NOT NULL, STATUS ENUM('waiting','paid','cancelled','refunded') ); 

Счётчик CURRENT_COUNT обновляется только по статусу paid — предварительные участия без оплаты не считаются.

Защита от race condition

Главная инженерная проблема — одновременное присоединение участников. Если два клиента читают CURRENT_COUNT = 9 при MIN_PARTICIPANTS = 10, оба могут стать «активирующим» участником. Защита — атомарное обновление:

use Bitrix\Main\Application; $connection = Application::getConnection(); $connection->startTransaction(); try { $row = $connection->query( "SELECT * FROM b_group_deal WHERE ID = {$dealId} AND STATUS = 'active' FOR UPDATE" )->fetch(); if (!$row || strtotime($row['DATE_END']) < time()) { $connection->rollbackTransaction(); return ['error' => 'Deal not available']; } $connection->query( "INSERT INTO b_group_deal_participant (DEAL_ID, USER_ID, DATE_ADD, STATUS) VALUES ({$dealId}, {$userId}, NOW(), 'waiting')" ); $connection->query( "UPDATE b_group_deal SET CURRENT_COUNT = CURRENT_COUNT + 1 WHERE ID = {$dealId}" ); $connection->commitTransaction(); } catch (\Exception $e) { $connection->rollbackTransaction(); throw $e; } 

После присоединения участник получает статус waiting. Оплата происходит двумя сценариями:

Сценарий A — предоплата: участник сразу оформляет заказ и платит. Если акция не набирает MIN_PARTICIPANTS к DATE_END, деньги возвращаются автоматически через обработчик агента.

Сценарий B — отложенный заказ: участник бронирует место без оплаты. При достижении минимума всем участникам уходит уведомление с предложением оформить заказ по сниженной цене. Дедлайн — 24–48 часов.

Сравнение сценариев:

Сценарий Оплата Риск для покупателя Риск для магазина
Предоплата Сразу Деньги заморожены до завершения акции Возвраты при провале
Отложенный заказ После достижения минимума Нет риска, но нужно следить за уведомлением Часть участников может не оформить заказ

Ценообразование и скидки

Текущая скидка рассчитывается динамически по таблице b_group_deal_tier. Нельзя использовать стандартную систему скидок b_catalog_discount — она не умеет работать с динамическим счётчиком. Расчёт активного тира:

function getActiveTier(int $dealId, int $currentCount): ?array { $connection = Application::getConnection(); return $connection->query( "SELECT * FROM b_group_deal_tier WHERE DEAL_ID = {$dealId} AND PARTICIPANTS_FROM <= {$currentCount} ORDER BY PARTICIPANTS_FROM DESC LIMIT 1" )->fetch() ?: null; } 

При добавлении в корзину цена подставляется через обработчик OnSaleBasketItemRefreshData.

Визуальный прогресс-бар

Компонент прогресса — AJAX-виджет, обновляемый каждые 30 секунд. Данные отдаёт контроллер:

// /local/ajax/group-deal-status.php $deal = $connection->query( "SELECT gd.*, gt.DISCOUNT_PERCENT, gt.PARTICIPANTS_FROM as NEXT_TIER FROM b_group_deal gd LEFT JOIN b_group_deal_tier gt ON gt.DEAL_ID = gd.ID AND gt.PARTICIPANTS_FROM > gd.CURRENT_COUNT WHERE gd.ID = {$dealId} ORDER BY gt.PARTICIPANTS_FROM ASC LIMIT 1" )->fetch(); header('Content-Type: application/json'); echo json_encode([ 'current' => (int)$deal['CURRENT_COUNT'], 'next_tier' => (int)$deal['NEXT_TIER'], 'discount' => (float)$deal['DISCOUNT_PERCENT'], 'time_left' => strtotime($deal['DATE_END']) - time(), ]); 

Прогресс-бар отображает процент current / next_tier * 100 и показывает, сколько участников нужно до следующей ступени скидки.

Завершение акции и возвраты

Агент CAgent запускается каждые 5 минут (см. Bitrix Documentation) и проверяет акции с истёкшим DATE_END:

  • Если CURRENT_COUNT >= MIN_PARTICIPANTS → статус success. Участники с waiting получают задание оформить заказ.
  • Если CURRENT_COUNT < MIN_PARTICIPANTS → статус failed. Участникам с paid выполняется возврат через \Bitrix\Sale\PaySystem\Manager::refund().

Уведомления отправляются через \Bitrix\Main\Mail\Event::send() с кастомными шаблонами.

Что входит в работу

  • Анализ требований и проектирование схемы данных
  • Реализация кастомных таблиц и ORM-моделей
  • Настройка агентов и почтовых шаблонов
  • Интеграция с платёжными системами (54-ФЗ, ОФД)
  • Тестирование на конкурентную нагрузку
  • Документация и обучение администраторов
  • Гарантийная поддержка после запуска

Сроки реализации

Масштаб Функционал Срок
MVP (одна акция, одна ступень скидки, ручное управление) HL-блок + обработчики + AJAX-счётчик 1–1.5 недели
Полная система (тиры, агенты, возвраты, ЛК участника) Кастомные таблицы + транзакции + модуль + почтовые шаблоны 2–3 недели
Маркетплейс акций (несколько поставщиков, витрина) Полноценный модуль с административным интерфейсом + API 4–5 недель

Нагрузочное тестирование и безопасность

Перед запуском системы групповых покупок необходимо протестировать конкурентный сценарий: несколько десятков одновременных присоединений к одной акции. Без этого race condition обнаруживается только на реальном трафике, когда CURRENT_COUNT выходит за пределы MAX_PARTICIPANTS.

Тест: инструментом Apache JMeter или k6 имитируем 50 одновременных запросов к endpoint-у присоединения. Ожидаемый результат — ровно MAX_PARTICIPANTS записей в b_group_deal_participant со статусом waiting. Если записей больше — транзакции не отрабатывают корректно. Проверьте уровень изоляции MySQL (REPEATABLE READ) и наличие блокировки FOR UPDATE в запросе выборки акции. При использовании MariaDB дополнительно убедитесь, что включён режим строгих транзакций (STRICT_TRANS_TABLES).

Дополнительно: добавьте rate limiting на endpoint присоединения — не более 3 запросов с одного IP в секунду. Это защищает от ботов, которые могут заполнить акцию фиктивными участниками.

Свяжитесь с нами для оценки вашего проекта. Получите консультацию по реализации системы групповых покупок.