Tilda · 7 мин чтения

Промокод на определённые товары в Тильде: как ограничить скидку в корзине

Промокод на определённые товары в Тильде из коробки не настраивается: встроенный модуль скидок в Tilda Cart применяет код либо ко всей сумме заказа, либо не применяет вообще. За практику с Tilda-магазинами я закрывал эту задачу скриптами больше десяти раз: то нужна скидка только на одну категорию, то на конкретные артикулы, то промокод должен игнорировать товары, которые уже участвуют в акции. Разберу, как устроена корзина Tilda изнутри и какой скрипт решает задачу без переписывания сайта.

Как устроены промокоды в Tilda и почему они не различают товары

Стандартная настройка находится в панели сайта: Настройки корзины, Промокоды. Там задаётся код, тип скидки (процент, фиксированная сумма или бесплатная доставка), даты действия, лимит использований и минимальная сумма заказа. Для акции вроде «скидка 10% на весь заказ по коду NEWYEAR» с лимитом в 200 применений и сроком действия 30 дней этого достаточно, панель всё посчитает сама.

Проблема в том, что скидка считается от итоговой суммы корзины целиком. Поля «категория» или «товар» в настройках промокода нет. Если магазин продаёт и одежду, и обувь, а скидка должна работать только на одежду, стандартный промокод такое не умеет: он либо скинет процент со всей корзины, включая обувь, либо придётся вручную вычитать сумму на кассе, что для интернет-магазина не вариант, особенно если заказы идут через автоматическую отправку на оплату.

Логика скрипта: как ограничить промокод конкретными товарами

Корзина Tilda работает через глобальный объект window.tcart, в котором лежит массив tcart.products с полями каждого товара: uid, sku (артикул), name, price, amount (количество). Отдельного поля «категория» в этом объекте обычно нет, поэтому категорию я определяю либо по префиксу артикула (например, все товары раздела «Одежда» имеют артикулы вида CLTH-101), либо по дополнительному текстовому полю товара, которое заполняется в панели Tilda и попадает в разметку карточки.

При изменении состава корзины Tilda генерирует события обновления, на которые можно подписаться и пересчитать скидку не от полной суммы, а только от суммы товаров из разрешённого списка. Схема простая: скрипт слушает изменение корзины, проходит по tcart.products, суммирует стоимость только тех позиций, чей артикул или категория входят в допустимый список, считает скидку от этой суммы и подставляет результат в поля, которые Tilda использует при отрисовке итога, вместо стандартного расчёта скидки от всей суммы.

Пример: скидка только на одну категорию товаров

document.addEventListener('tcart:updated', function () {
  var allowedSku = ['CLEAR-101', 'CLEAR-102', 'CLEAR-205'];
  var eligibleSum = 0;

  window.tcart.products.forEach(function (product) {
    if (allowedSku.indexOf(product.sku) !== -1) {
      eligibleSum += product.price * product.amount;
    }
  });

  var discount = Math.round(eligibleSum * 0.15);

  window.tcart.discount = discount;
  window.tcart.amount = window.tcart.amount - discount;

  if (typeof tcart__reDrawCart === 'function') {
    tcart__reDrawCart();
  }
});

У Tilda несколько версий корзины, и событие может называться иначе, а пересчёт срабатывать через tcart__updateProductCallback вместо кастомного события. Перед тем как ставить скрипт на боевой сайт, я всегда смотрю в исходный код конкретного проекта, какая версия корзины подключена, как называются нужные хуки и в каком формате приходит артикул: с пробелами, в верхнем регистре или с дефисами внутри.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Комбинация условий: категория, минимальная сумма, исключение акционных товаров

На практике редко нужен просто промокод на категорию. Обычно условия комбинируются:

  • минимальная сумма заказа, например от 3000 ₽, до которой промокод вообще не активируется;
  • исключение товаров, которые уже продаются со скидкой (у них в Tilda обычно заполнено поле старой цены priceold, отличное от price);
  • ограничение по нескольким категориям одновременно, если магазин делит товары по разделам каталога, а не только по артикулам;
  • лимит на количество товаров в заказе, на которые распространяется скидка, если промокод рассчитан на пробную покупку, а не на опт.

Пример из недавнего проекта: промокод «SUMMER15» давал минус 15% только на категорию «Одежда», требовал корзину от 3000 ₽ и не срабатывал на товарах, у которых уже была указана старая цена. Такие условия пишутся внутри того же обработчика: сначала фильтр по allowedSku собирает подходящие позиции, потом отдельной проверкой сравнивается сумма этих позиций с порогом, и только после этого высчитывается сама скидка. Порядок проверок важен, потому что если считать скидку раньше проверки минимальной суммы, при удалении товара из корзины старое значение скидки может остаться висеть в интерфейсе до следующего пересчёта.

Тестирование скрипта и типичные ошибки

На доработках корзины Tilda я регулярно натыкаюсь на одни и те же грабли:

  • скрипт запускается раньше, чем Tilda успевает инициализировать tcart, и падает с ошибкой в консоли, поэтому вызов стоит оборачивать в проверку typeof window.tcart !== 'undefined' или дожидаться готовности корзины;
  • пересчёт срабатывает один раз при загрузке страницы, а не при каждом добавлении или удалении товара, поэтому обработчик нужно вешать на все события изменения корзины, а не только на DOMContentLoaded;
  • на мобильном Safari корзина иногда подтягивается из кэша, и старая скидка «залипает» от предыдущей сессии, поэтому состояние скидки стоит сбрасывать при каждом событии обновления;
  • скидка не отображается отдельной строкой в блоке товара, только в итоговой сумме, из-за чего покупатели пишут в поддержку, что промокод не сработал, хотя сумма посчитана верно, поэтому лучше выводить строку вида «Скидка по промокоду: ‑450 ₽» рядом с итогом;
  • письмо с подтверждением заказа собирается Tilda по своему шаблону и может показать сумму без учёта кастомной скидки, если скидка не передана в те же переменные, что использует шаблон письма.

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

Почему проверку нельзя оставлять только на клиенте

JS-скрипт в корзине решает задачу для честного покупателя, но выполняется в браузере, а значит любой человек с открытой консолью разработчика может вручную поменять значение window.tcart.discount и применить скидку ко всей корзине перед отправкой заказа. Для интернет-магазина с реальными оборотами это не гипотетический риск, а конкретная дыра в скидочной логике.

Рабочая схема защиты: Tilda отправляет вебхук при создании заказа, и на сервере, напрямую или через сценарий в n8n, повторно проверяются товары в заказе, их сумма и условия промокода, прежде чем сформировать ссылку на оплату через T‑Bank или передать заказ в СДЭК на доставку. Если проверка не сходится с тем, что видел клиент в браузере, заказ уходит в статус «на проверке» вместо автоматического списания, а в telegram-бот на aiogram, который я обычно подключаю для таких магазинов, падает уведомление менеджеру: какой заказ, какой промокод и что именно не сошлось. Такую интеграцию промокода с CRM и приёмом оплаты я обычно делаю отдельным блоком поверх клиентского скрипта, а не вместо него.

Сравнение: Tilda, WooCommerce и кастомные решения

Платформа Ограничение промокода по товару из коробки Нужен кастомный скрипт Серверная проверка перед оплатой
Tilda Нет, скидка считается от всей корзины Да, JS в блоке корзины Да, отдельным вебхуком
WooCommerce Да, купоны привязываются к товару или категории в настройках Обычно не нужен Частично, через хуки WooCommerce
Самописный магазин на React и Laravel Полностью гибко, логика пишется под задачу Да, это часть бэкенда, а не надстройка Да, изначально на сервере

Разница объясняется устройством платформ: WooCommerce изначально построен вокруг гибкой системы купонов с привязкой к товару, категории и даже пользователю, потому что это движок для интернет-магазинов, а Tilda в первую очередь конструктор сайтов, где корзина появилась как надстройка. У сторонних студий разработки доработка корзины Tilda под ограниченный промокод на рынке стоит по-разному в зависимости от сложности условий, обычно от 15 000 до 40 000 ₽. У меня простая доработка с одной категорией и одним типом скидки стоит от 3 000 ₽, а комплексная интеграция с проверкой на сервере, CRM и эквайрингом от 40 000 ₽.

Частые вопросы

Можно ли ограничить промокод на товары в Тильде без программиста?

Встроенными настройками нет. Срок действия и лимит использований задаёшь в панели без кода, а фильтр по конкретным товарам или категориям, как и минимальная сумма заказа, добавляется только скриптом в блоке корзины.

Промокод продолжит работать после того, как я поменяю дизайн блока корзины?

Если меняется верстка блока корзины, витрины каталога или Zero Block, структура товаров и события могут отличаться, и скрипт стоит проверить заново на конкретной версии блока. Это отдельная задача, которая не входит в стоимость исходной доработки.

Как проверить, что промокод не применяется к товарам не из списка?

Добавляю в корзину и товар из разрешённого списка, и товар вне списка, применяю код и сверяю, что скидка посчиталась только от суммы первого. Дополнительно открываю консоль разработчика и вывожу window.tcart.products вместе с посчитанной скидкой, чтобы убедиться в правильном списке артикулов.

Можно совместить ограничение по товарам с минимальной суммой заказа?

Да, это просто ещё одно условие в том же обработчике: сначала фильтруется список товаров, подходящих под промокод, потом от их суммы проверяется порог, например от 3000 ₽, и только тогда скидка применяется к корзине.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно