Корзина и оплата не сошлись. Кто ошибся?
Три разных товара по 99,90 ₽, скидка 15%. Корзина показывает 254,76 ₽, на оплате — 254,74 ₽. Первая мысль: опять double, надо хранить деньги в копейках.
Но здесь точная арифметика не устранит расхождение. Можно округлить цену каждого товара после скидки и получить 254,76 ₽. Можно округлить общую скидку — 44,96 ₽ — и вычесть её из 299,70 ₽: останется 254,74 ₽. Если сразу округлить общий остаток после скидки, получится 254,75 ₽.
Это условная корзина, но все три вычисления конкретны. Везде половина копейки округляется вверх. Различается то, к чему применили правило. Требование «два знака после запятой» этого выбора не содержит.
Проблема становится заметнее при возврате. За товары заплатили 254,74 ₽. Если каждый из трёх возвратов заново считать как 99,90 × 0,85, получится по 84,92 ₽: суммарно больше оплаченного. Последний запрос остановит ограничение платежа, а объяснять странную стоимость последнего товара придётся поддержке.
В выбранной для разбора модели общая скидка заранее распределяется по позициям. Целые копейки выдаются вниз, остаток — строкам с наибольшими дробными долями, с постоянными ID для разрешения ничьей. Здесь две позиции получают скидку 14,99 ₽, третья — 14,98 ₽. Оплаченные стоимости: 84,91 ₽, 84,91 ₽ и 84,92 ₽. Вместе ровно 254,74 ₽.
Сортировка интерфейса не должна переносить копейку между товарами. Возврат использует сохранённую оплаченную долю, а не сегодняшнюю цену из каталога. Для нескольких единиц одной позиции нужно отдельно определить распределение внутри строки; для реальных возвратов — согласованные условия и защита от повторного исполнения.
С доставкой расхождение может оказаться крупнее. Если она бесплатна от 280 ₽, какую сумму сравнивать с порогом? В этой корзине до скидки 299,70 ₽, после — 254,74 ₽. Ответ меняет уже всю стоимость доставки. Decimal-пакет не примет это решение за бизнес.
Поэтому серверу мало вернуть один правильный total. Нужен расчёт, в котором строки, скидка и доставка складываются в итог, а показанные условия связаны с версией корзины. Клиент может считать предварительную цену для быстрого отклика, но не должен незаметно подтверждать другую сумму.
Разбор ArkTelos Lab начинается не с выбора числового типа, а с правил заказа. В полной статье — таблицы, Dart-код, пример ответа сервера и проверенные расчёты. Проверялась только арифметика, без платёжных вызовов и проверки юридических условий.
ArkTelos · Lab RU · Lab EN · ArkTelos RU · ArkTelos EN.