Промокоды в интернет-магазине - самый быстрый способ поднять конверсию на короткой акции, но именно на них чаще всего теряют деньги: код без лимита разлетается по агрегаторам скидок, а простой числовой код перебирают скриптом за полчаса. На проектах на WooCommerce и Tilda я настраивал промокоды и для разовых акций, и для постоянных программ лояльности - ниже про правила, лимиты и защиту от подбора, которые закладываю сразу, а не после того, как акция уже слита в паблик со скидками.
Зачем промокоды в интернет-магазине и какие типы встречаются
Промокод решает три разные задачи, и от того, какую именно закрывает конкретный код, зависит логика проверки:
- привлечение новых клиентов - скидка на первый заказ, код раздаётся в рекламе или блогерам;
- удержание и повторные покупки - персональный код в письме после заказа, часто с ограничением на одно применение;
- управление остатками - скидка на конкретную категорию или коллекцию, чтобы разгрузить склад к сезону;
- партнёрские и реферальные программы - именной код, который отслеживает, кто привёл покупателя.
Каждый тип требует своих правил: код для рекламной кампании должен работать без привязки к личности, а персональный код в письме - ровно наоборот, привязываться к email или телефону и сгорать после первого применения. Смешивать логику в одной сущности «промокод» без разделения типов - типичная причина, почему акция потом даёт скидку тем, кому она не предназначалась.
Правила начисления скидки: проценты, сумма, бесплатная доставка
Процентная скидка считается от суммы корзины до или после применения других скидок - это нужно решить до запуска, иначе на товарах, уже участвующих в распродаже, скидка задвоится. Я закладываю правило: промокод не суммируется со скидками по акции, а применяется к полной цене товара, если явно не оговорено обратное.
Фиксированная сумма скидки требует нижней границы корзины - иначе при заказе на 300 ₽ с промокодом на 500 ₽ магазин уходит в минус. В WooCommerce это настраивается прямо в правилах купона на вкладке ограничений использования, где задаётся минимальная и максимальная сумма заказа для применения кода - переопределять эту логику руками не нужно.
Бесплатная доставка как тип промокода отдельно завязана на расчёт стоимости доставки. В Tilda стоимость по зонам считается встроенным калькулятором СДЭК в настройках корзины, и промокод на бесплатную доставку в таком случае должен обнулять именно эту рассчитанную сумму, а не просто скрывать поле оплаты доставки - иначе на этапе оформления заказа в СДЭК всё равно уйдёт платное отправление.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
Лимиты использования: по коду, по клиенту и по времени
Лимит - это не одна настройка, а минимум четыре разных ограничения, и они комбинируются друг с другом:
| Тип лимита | Что ограничивает | Типичный сценарий |
|---|---|---|
| Общий лимит использований | Сколько раз код сработает суммарно по всем покупателям | Акция на первые 50 заказов |
| Лимит на клиента | Сколько раз один email или телефон применит код | Скидка только на первый заказ |
| Лимит по времени | Период действия кода с автоматическим отключением | Промокод на выходные или на 48 часов |
| Лимит по сумме заказа | Минимальная или максимальная сумма корзины | Скидка только от 3000 ₽ в корзине |
В WooCommerce первые два лимита - штатная функция купонов: поля «Лимит использования купона» и «Лимит использования на одного пользователя» в настройках ограничений, без доработок.
В Tilda есть встроенное ограничение количества использований промокода в настройках корзины - оно закрывает общий лимит по коду. Но это глобальный счётчик: он не умеет считать использования на конкретного покупателя и не привязывается к телефону или email, поэтому код на «один раз для новых клиентов» через него не собрать. Для такого сценария я делал ограничение промокодов в Tilda с привязкой лимита к контакту покупателя поверх штатной корзины.
Защита промокода от подбора и накрутки
Короткий числовой код вроде «1234» или последовательные коды «SALE01», «SALE02» перебираются скриптом за минуты - форма проверки купона обычно не защищена от частых запросов, и если бэкенд просто сверяет строку с таблицей без ограничений, злоумышленник переберёт весь диапазон циклом.
Что закладываю на уровне сервера:
- длина и алфавит кода - минимум 8 символов из букв и цифр, без предсказуемых последовательностей и без человекочитаемых номеров заказов внутри кода;
- ограничение частоты запросов к обработчику применения купона - не больше нескольких попыток в минуту с одного IP или сессии;
- капча или временная блокировка после нескольких неудачных попыток подряд, а не после каждой отдельной попытки - иначе капча мешает обычным покупателям, которые просто ошиблись в коде;
- логирование неудачных попыток с IP и user-agent, чтобы отличить одного человека, который путает раскладку, от скрипта, перебирающего тысячи вариантов;
- персональные коды выдаю не строкой в письме, а одноразовым токеном, который сгорает при первом успешном применении независимо от статуса оплаты заказа.
Как проверяю и храню промокоды на практике
Логика проверки и списания промокода - обычная задача для PHP-обработчика и MySQL на том же хостинге, где уже развёрнут магазин: отдельный сервер под это поднимать не нужно. Таблица промокодов с уникальным индексом по коду и счётчиком использований, атомарное обновление в момент применения:
UPDATE promocodes
SET used_count = used_count + 1
WHERE code = 'SALE2026XZ'
AND used_count < usage_limit
AND (expires_at IS NULL OR expires_at > NOW());
Если запрос обновил ноль строк - лимит уже исчерпан или срок истёк, и скидка не применяется. Это защищает от гонки, когда два покупателя одновременно оформляют заказ с последним доступным промокодом: без атомарного UPDATE оба обработчика могут прочитать один и тот же остаток лимита и применить скидку дважды.
Финально списывать использование стоит после подтверждения оплаты, а не в момент оформления заказа - иначе брошенные корзины с промокодом в T‑Bank или другом эквайринге съедают лимит акции впустую. Резервирую код на заказ на короткое время и снимаю резерв, если оплата не пришла.
Для мониторинга накрутки удобно не городить отдельный сервис, а собрать уведомление в Telegram: если пишу Telegram-бота для проекта, беру aiogram именно из-за асинхронной обработки и удобной работы с вебхуками - бот присылает администратору сообщение, когда по одному коду за короткое время прилетело аномально много неудачных попыток. Похожую цепочку я собираю в n8n, когда нужно не писать бота с нуля, а просто соединить вебхук магазина с оповещением в Telegram и записью инцидента в таблицу для разбора.
Частые ошибки при запуске промокодов
За практику на разных магазинах чаще всего встречаются одни и те же промахи:
- забыли выставить срок действия - код продолжает работать месяцами после окончания акции, и о нём узнают из чужого блога со скидками;
- не проверили пересечение со скидками по акции - промокод суммируется с распродажей, и товар уходит по цене ниже себестоимости;
- не учли часовой пояс при задании даты окончания - код отключается на несколько часов раньше или позже, чем планировали;
- не протестировали применение купона в мобильной версии корзины - поле ввода промокода спрятано за дополнительным кликом, и конверсия акции падает не из-за условий, а из-за интерфейса;
- код и остаток лимита видны в ответе фронтенда или в исходном JS - достаточно открыть консоль браузера, чтобы вытащить действующий промокод раньше официального анонса.
Частые вопросы
Сколько стоит внедрить промокоды в интернет-магазин под ключ?
Интернет-магазин с корзиной, промокодами, лимитами и защитой от подбора я делаю от 80 000 ₽. Если промокоды нужно добавить в уже готовый магазин на Tilda как отдельную доработку - это кастомная интеграция от 40 000 ₽, конкретная цена зависит от того, сколько типов лимитов и правил нужно заложить.
Можно ли ограничить промокод одним использованием на человека в Tilda?
Штатное ограничение количества использований в Tilda считает только общий лимит по коду для всех покупателей сразу, без привязки к конкретному человеку. Чтобы код сработал у каждого покупателя не больше одного раза, нужна отдельная привязка лимита к email или телефону поверх стандартной корзины.
Как защитить промокод от перебора без капчи на каждый заказ?
Капчу имеет смысл показывать не сразу, а после нескольких подряд неудачных попыток ввода с одного IP или сессии - тогда обычный покупатель, который один раз ошибся в коде, её не увидит, а скрипт, перебирающий варианты по кругу, упрётся в ограничение. Вместе с ограничением частоты запросов и логированием попыток этого обычно достаточно без капчи на каждую попытку.
Что делать, если промокод уже слили в паблик со скидками?
Быстрее всего отключить код и выпустить новый с более длинным и непредсказуемым значением, а не пытаться заблокировать конкретные IP - код разлетается через скриншоты и репосты, и по IP отследить всех, кто им воспользовался, не получится. На будущее закладываю лимит использований заранее, чтобы даже слитый код не смог принести акции убыток больше расчётного.