Skip to content

Medusa: кнопка «убрать скидку» выдавала максимальную, потому что ноль в JavaScript ложный

v1 0 stars 0 forks 0 watchers 1 branch 0 runs Public
mikicreated via APIv1

Medusa, плагин лояльности, август 2026.

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

А чтобы вообще убрать списание, передайте ноль.

Одна строка
ts
1input.amount ? input.amount : storeCreditAccount.balance

Читается как «если сумму передали — берём её, иначе весь остаток». И это верно ровно до того дня, когда кто-то передаст ноль.

Ноль в JavaScript ложный. Проверка решает, что суммы не было, и подставляет balanceвесь остаток покупателя. Из описания исправления #16378:

Clearing the store credit from a cart left it with a credit line for everything the customer had.

Очистка списания с корзины оставляла её со списанием на всё, что у покупателя было.

Кнопка «убрать скидку» выдавала максимальную скидку. Механика, за которую в другом месте платят маркетологам.

Починка была уже написана — тремя строками ниже

Самое поучительное здесь не сама ошибка, а то, где нашлось лекарство. Правка (#16379) — одно слово:

It updates the fallback condition in computeCreditLineActionsStep to strictly check if the value is defined, rather than relying on a truthiness check.

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

Changed the guard to isDefined(input.amount), which is the same check the balance validation a few lines below already uses.

…ровно та проверка, которую проверка остатка несколькими строками ниже уже использует.

То есть верный образец лежал в том же файле, в пределах экрана. Просто в одном месте написали ?, а в другом — isDefined.

Три человека, три дня

Ошибку чинили трижды за три дня: #16378 и #16379 — 9 августа, #16401 — 11-го. Разные авторы, одна строка.

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

Продолжение: то же место, другая беда

Через два дня в том же шаге нашли второе (#16405). Удаление старой строки списания и создание новой были обёрнуты в parallelize(...) — выполнялись одновременно. Хотя документация того же шага говорит: «removes any existing store-credit lines and creates a new credit line» — то есть сначала одно, потом другое.

That mismatch is consistent with a cart intermittently keeping a stale credit amount when the workflow is called again with a new amount.

Это расхождение объясняет, почему корзина иногда сохраняла прежнюю сумму списания.

Расхождение между документацией шага и его реализацией прожило до первого плавающего бага. Плавающие баги на то и плавающие, что живут долго.

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

1
Найти числовые поля в проверках на истинность

Ищите amount ?, price &&, count ? … : — всякое место, где число проверяется на истинность, а не на существование.

Why: Ноль — настоящее значение и обычно означает «не надо». Проверка на истинность читает его как «не передали» — и подставляет запасной вариант, который почти всегда максимальный.
Check
  • Каждое найденное место либо исправлено, либо помечено как намеренное — с объяснением
  • Проверены не только суммы: цена 0 (акция), количество 0, порог 0
2
Посмотреть, нет ли верной проверки тут же, рядом

Прежде чем придумывать свою — прочитайте соседние двадцать строк. В Medusa образец лежал в том же файле, в пределах экрана.

Why: Две разные проверки одного и того же в одном файле — это не стиль, а будущая ошибка: одна из них неверна, и непонятно какая.
Check
  • В правленном файле осталась ОДНА форма проверки на существование
3
Сверить документацию шага с тем, что он делаетRecommended

Если написано «сначала убрать, потом создать», а в коде оба действия идут параллельно — неправ код.

Why: Расхождение между описанием и реализацией не болит до первого плавающего бага, а плавающие баги живут долго и стоят дорого.
Check
  • Либо код приведён к описанию, либо описание исправлено — и сказано, кто из них врал

Medusa: защита от повтора отказывает там, где можно было ответить, и пропускает там, где повтор стоит денег

v1Public 0 0
updated Sep 1, 2026

Найти в открытом проекте настоящую историю поломки, восстановить ход мысли по написанному и закончить проверкой своего кода

v4Public 0 0
updated Sep 1, 2026

graphile-worker: очередь на Postgres, замеры в комментариях и настройка, не пережившая полутора лет

v2Public 0 0
updated Sep 1, 2026

Saleor: три человека за три года просят денег без копеек, потому что таких монет нет в обращении

v1Public 0 0
updated Sep 1, 2026

Better Auth: ограничение частоты считалось от последнего запроса, включая отклонённые, — и разблокировка не наступала никогда

v1Public 0 0
updated Sep 1, 2026

Как Cal.com год чинил расписание, переходящее за полночь, и чем это кончилось

v2Public 0 0
updated Sep 1, 2026