vvs35.ru боты, парсеры, серверы
УслугиГотовые решенияБлог принимаем заказы

Промокод, который увеличивал сумму заказа

Скидка 10 % сделала заказ дороже на 87 рублей. Код при этом был написан без единой ошибки — неправильной оказалась последовательность действий.

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

Кладу в корзину два товара на 1630 ₽. Доставка бесплатная, порог — от 1500 ₽. Ввожу промокод на 10 %, жму «Применить».

Итог: 1717 ₽. Скидка увеличила сумму на 87 рублей.

Почему так вышло

Логика была написана буквально по постановке задачи, строчка за строчкой:

const disc = Math.round(sum * promoPct / 100);   // скидка 163 ₽
const dlv  = (sum - disc) >= 1500 ? 0 : 250;     // доставка от суммы со скидкой
const total = sum - disc + dlv;

Считаем по шагам. Товаров на 1630. Скидка 10 % — это 163 рубля. После скидки остаётся 1467, а это уже ниже порога бесплатной доставки. Значит, добавляются 250 рублей за доставку. Итого 1467 + 250 = 1717.

Формально всё верно: скидка применилась, порог проверен, доставка посчитана. Ни одна строка не содержит ошибки. Ошибка — в порядке действий.

Как правильно

Порог бесплатной доставки нужно считать от суммы товаров до скидки:

const disc = Math.round(sum * promoPct / 100);
const dlv  = sum >= 1500 ? 0 : 250;   // порог — от суммы ДО скидки
const total = sum - disc + dlv;
былостало
Товары1630 ₽1630 ₽
Скидка 10 %−163 ₽−163 ₽
Доставка+250 ₽0 ₽
Итого1717 ₽1467 ₽

Правка на одну строку. Найти её без прогона сценария — почти невозможно.

Почему это не мелочь

Такой баг не роняет сайт и не пишет ошибку в лог. Он молча делает ровно то, за что покупатель обижается сильнее всего: обещает скидку и берёт больше.

Дальше сценарий предсказуемый. Человек вводит промокод из рассылки, видит, что сумма выросла, и либо уходит совсем, либо пишет в поддержку — и там уже начинается разбирательство про «обман с акцией». Владелец магазина узнаёт о проблеме последним, потому что в аналитике это выглядит как обычные брошенные корзины.

Отдельная неприятность: даже когда порог считается правильно, поведение остаётся неочевидным для покупателя. Заказ на 1550 со скидкой 10 % опускается до 1395 — и человек искренне не понимает, почему доставка вдруг стала платной, если на странице написано «бесплатно от 1500». Поэтому одной правки мало: в корзине должно быть видно, что доставка считается от суммы до скидки. Одна строчка текста снимает половину обращений в поддержку.

Где ещё прячется этот класс ошибок

Порядок операций — вообще главный источник тихих багов в корзине. Проверьте у себя:

Скидка и доставка

Разобрали выше. Порог — от суммы товаров, иначе промокод может удорожать заказ.

Два промокода подряд

Складываются они или умножаются? Скидка 20 % и ещё 20 % — это 40 % или 36 %? Оба варианта встречаются в жизни, но решение должно быть осознанным, а не случайным следствием того, в каком порядке лежат строки кода.

Скидка и бонусные баллы

Если баллы списываются после процентной скидки, покупатель теряет часть их ценности. Если до — теряет магазин. Универсально правильного ответа нет, но у бизнеса он должен быть один, а не разный на сайте и в приложении.

Округление

Скидка 33 % от 100 рублей — это 33 рубля или 33,33? Округлять каждую позицию или итог? Расхождение в рубль на позицию превращается в заметную сумму на чеке из тридцати товаров, и потом это всплывает при сверке с бухгалтерией.

Сумма считается на клиенте

Отдельный и куда более опасный случай: если итог приходит с фронтенда, его можно подменить через инструменты разработчика. В демо я специально считаю всё на сервере и цену с клиента не принимаю вовсе — иначе любой промокод превращается в «введи что хочешь».

Что с этим делать

Дешёвый способ поймать такие вещи — прогнать три-четыре сценария руками до того, как магазин увидят люди:

  1. Заказ чуть выше порога бесплатной доставки со скидкой — тот самый случай.
  2. Заказ чуть ниже порога без скидки.
  3. Заказ ровно на границе.
  4. Два промокода, если они допускаются.

Пятнадцать минут работы. Мой баг нашёлся на первом же сценарии.

И общий вывод, который стоит дороже конкретной строчки кода: бизнес-логику проверяют цифрами, а не чтением кода. Код здесь был написан правильно. Неправильной была последовательность, а её видно только на живых числах.


Демо, в котором это случилось, открыто: магазин с промокодом DEMO10 и админка, где меняются товары и цены. Можете проверить сами — сейчас считает верно.

Коротко: частые вопросы

Почему скидка по промокоду увеличила сумму заказа?

Порог бесплатной доставки проверялся по сумме после скидки. Корзина на 1 630 ₽ порог 1 500 ₽ проходила, после скидки 163 ₽ оставалось 1 467 ₽ — порог переставал выполняться, и добавлялась доставка 250 ₽. Итог 1 717 ₽ вместо 1 630 ₽.

Как правильно считать порядок скидки и доставки?

Порог бесплатной доставки считается от суммы товаров до скидки, а не после. Правка в одну строку: sum >= 1500 вместо (sum - disc) >= 1500. На тех же числах получается 1 467 ₽.

Почему два промокода по 10 % дают не 20 %?

Второй считается от уже уменьшенной суммы, и вместе они дают 19 %. Это не ошибка в коде, а последствие порядка действий. Какое поведение верное — решает заказчик, и спросить надо до того, как это увидит покупатель.

Как находить такие ошибки раньше покупателя?

Прогоном сценария покупки руками, с числами на границе: ровно на пороге, на рубль ниже и на рубль выше. Ошибка была не в формуле, а в порядке действий, и на чтении кода она не видна — каждая строка по отдельности правильная.

Делаете магазин или бота с заказами? Посмотрю вашу логику расчёта — чтобы такие вещи всплывали до запуска, а не после.

Написать в Telegram @vvs35_bot

Читать дальше