Разработка системы скидок и акций для интернет-магазина

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка системы скидок и акций для интернет-магазина
Средняя
~5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка системы скидок и акций для интернет-магазина

Система скидок — это не просто «зачёркнутая цена». Это движок, который управляет многоуровневыми правилами: акционными ценами, скидками по объёму, временными распродажами, комбинированными условиями. Неправильная реализация ведёт к конфликтам правил, некорректным итогам и дырам в марже. Разработка полноценной системы скидок занимает 5–9 рабочих дней.

Типы скидок и их приоритет

В любом магазине сосуществуют несколько видов скидок. Порядок применения критически важен:

  1. Акционная цена товара (sale_price на уровне SKU) — базовая, применяется всегда
  2. Скидка по категории — процент на все товары категории в период акции
  3. Скидка за объём — при покупке N единиц цена снижается
  4. Скидка по сегменту пользователя — VIP-клиенты, оптовики
  5. Промокод — поверх уже применённых скидок (или вместо них — зависит от правила)

Правило стекирования задаётся на уровне каждой акции: exclusive (не совмещается с другими), stackable (суммируется), override (отменяет остальные).

Модель данных акций

CREATE TABLE promotions (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    type VARCHAR(30) NOT NULL,
    -- 'product_discount', 'category_discount', 'volume_discount',
    -- 'bundle', 'bogo', 'tiered'
    priority INT DEFAULT 0,
    stackable BOOLEAN DEFAULT FALSE,
    conditions JSONB DEFAULT '{}',
    actions JSONB DEFAULT '{}',
    starts_at TIMESTAMP,
    ends_at TIMESTAMP,
    is_active BOOLEAN DEFAULT TRUE
);

conditions и actions в JSONB позволяют задавать произвольные правила без изменения схемы:

{
  "conditions": {
    "min_qty": 3,
    "product_ids": [101, 102, 103],
    "user_segments": ["wholesale"]
  },
  "actions": {
    "type": "percent_discount",
    "value": 15,
    "applies_to": "matching_items"
  }
}

Движок применения скидок

Класс DiscountEngine последовательно применяет правила к корзине:

class DiscountEngine
{
    /** @var PromotionRule[] */
    private array $rules = [];

    public function __construct(Collection $activePromotions)
    {
        foreach ($activePromotions->sortByDesc('priority') as $promo) {
            $this->rules[] = PromotionRuleFactory::make($promo);
        }
    }

    public function apply(Cart $cart): DiscountResult
    {
        $result = new DiscountResult($cart);

        foreach ($this->rules as $rule) {
            if (!$rule->matches($cart)) continue;
            if (!$rule->isStackable() && $result->hasDiscount()) continue;

            $result->addDiscount($rule->calculate($cart));

            if ($rule->isExclusive()) break;
        }

        return $result;
    }
}

Скидка за объём (volume pricing)

Ступенчатые цены за количество — частый запрос в B2C и особенно в B2B:

class VolumePricingRule implements PromotionRule
{
    public function calculate(Cart $cart): array
    {
        $discounts = [];
        foreach ($cart->items as $item) {
            $tier = $this->getTier($item->product_id, $item->quantity);
            if ($tier) {
                $discounts[] = [
                    'item_id'  => $item->id,
                    'amount'   => ($item->price - $tier->price) * $item->quantity,
                    'label'    => "Скидка за объём (×{$item->quantity})",
                ];
            }
        }
        return $discounts;
    }

    private function getTier(int $productId, int $qty): ?VolumeTier
    {
        return VolumeTier::where('product_id', $productId)
            ->where('min_qty', '<=', $qty)
            ->orderByDesc('min_qty')
            ->first();
    }
}

Акционные периоды с таймером

Для «горящих» акций на фронтенде отображается таймер обратного отсчёта. Текущая акционная цена передаётся из API вместе с временем окончания:

interface PriceData {
  regular_price: number;
  sale_price: number | null;
  sale_ends_at: string | null; // ISO datetime
}

Компонент таймера:

const SaleTimer = ({ endsAt }: { endsAt: string }) => {
  const [remaining, setRemaining] = useState(differenceInSeconds(parseISO(endsAt), new Date()));

  useEffect(() => {
    const interval = setInterval(() => {
      setRemaining(prev => Math.max(0, prev - 1));
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  const hours = Math.floor(remaining / 3600);
  const minutes = Math.floor((remaining % 3600) / 60);
  const seconds = remaining % 60;

  return (
    <div className="flex gap-1 font-mono text-red-600">
      <span>{String(hours).padStart(2, '0')}</span>:
      <span>{String(minutes).padStart(2, '0')}</span>:
      <span>{String(seconds).padStart(2, '0')}</span>
    </div>
  );
};

BOGO (Buy One Get One)

Акция «купи 2, получи 1 в подарок» реализуется отдельным правилом:

class BogoRule implements PromotionRule
{
    public function calculate(Cart $cart): array
    {
        $qualifying = $cart->items->filter(fn($i) => in_array($i->product_id, $this->productIds));
        $totalQty = $qualifying->sum('quantity');
        $freeQty = intdiv($totalQty, $this->buyQty); // 1 бесплатный за каждые N купленных

        // Берём самые дешёвые позиции как бесплатные
        $cheapestPrice = $qualifying->min('price');
        return [['amount' => $cheapestPrice * $freeQty, 'label' => 'Акция 2+1']];
    }
}

Отображение скидок на странице товара и в каталоге

  • Зачёркнутая оригинальная цена рядом с акционной
  • Бейдж «−20%» или «−500 ₽» — в зависимости от типа скидки
  • Для volume pricing — таблица ступенчатых цен на карточке товара
  • Фильтр «Только акционные» в каталоге — SQL-запрос с WHERE sale_price IS NOT NULL AND sale_ends_at > NOW()

Планировщик акций

Акции стартуют и завершаются автоматически через Laravel Scheduler:

// App\Console\Kernel
$schedule->command('promotions:activate')->everyMinute();
$schedule->command('promotions:expire')->everyMinute();

При активации акции кеш актуальных цен инвалидируется. Для крупных каталогов (10k+ SKU) инвалидация выполняется через очередь, а не синхронно — иначе spike нагрузки на Redis.

Отчётность по акциям

Admin-панель показывает по каждой акции: количество применений, общую сумму скидок, средний чек с акцией vs без, топ товаров в акции по выручке. Это минимально необходимые данные для оценки эффективности и принятия решений о повторном запуске.