Если возврат не учитывает использованные бонусы, клиент может получить больше фактически уплаченной суммы либо потерять бонусы, которые должны восстановиться. Особенную сложность создают частичные возвраты, когда скидка распределена между несколькими позициями.
Нельзя рассчитывать возврат только от текущей цены товара. Источником должна быть зафиксированная финансовая разбивка исходного заказа: деньги, бонусная скидка, промокод, доставка и округление по каждой позиции.
Сверьте денежный и бонусный контуры
Возврат денег и движение бонусного баланса должны быть связанными, но отдельными операциями.
- Возвращается полная цена товара вместо уплаченной части.
- Списанные бонусы не восстанавливаются после полного возврата.
- При частичном возврате скидка распределяется повторно.
- Повтор webhook создает второе начисление бонусов.
Почему возникает проблема
Ошибка возникает, когда система хранит только итог заказа и пытается заново вычислить прошлое распределение.
- Бонусная скидка не сохранена по строкам заказа.
- Частичный возврат использует текущие цены и правила.
- Денежный refund и бонусная операция не объединены бизнес-транзакцией.
- Округление выполняется отдельно на каждом повторном расчете.
- Нет защиты от повторной обработки события платежной системы.
Пошаговая диагностика
Возьмите один заказ и восстановите исходную раскладку до копейки и бонуса.
- Сверьте сумму оплаты, бонусную скидку и скидки по позициям.
- Проверьте уже возвращенное количество и сумму.
- Найдите записи ledger бонусной программы.
- Сравните запрос refund и ответ платежной системы.
- Проверьте event ID и повторные попытки операции.
Храните распределение скидки в заказе
Исторический заказ должен оставаться неизменным даже после обновления цен и правил лояльности.
- Каждая позиция хранит денежную и бонусную часть.
- Округляющая разница назначается детерминированно.
- Возврат учитывает уже возвращенное количество.
- Восстановление бонусов имеет ссылку на исходное списание.
Как исправить проблему
Исправьте расчет на основе snapshot заказа и добавьте идемпотентность обеих операций.
- Сохраните распределение скидок и бонусов по строкам.
- Рассчитывайте частичный возврат от остатка конкретной позиции.
- Создавайте денежную и бонусную операции с общим refund ID.
- Защитите повтор webhook уникальным event ID.
- Сверьте и исправьте затронутые заказы отдельным отчетом.
Как проверить результат
- Клиент получает не больше фактически уплаченной суммы.
- Бонусный баланс меняется один раз и объяснимо.
- Частичные возвраты в сумме не превышают заказ.
- Аудит связывает refund с исходной оплатой и списанием.
Типичные ошибки
- Использовать текущую цену товара.
- Восстанавливать все бонусы при возврате одной позиции.
- Обновлять баланс без ledger-записи.
- Повторять refund после timeout без проверки провайдера.
Как предотвратить повторение
- Храните immutable snapshot финансов заказа.
- Тестируйте полные и частичные возвраты.
- Используйте idempotency key для денег и бонусов.
- Добавьте ежедневную сверку балансов.
Когда нужна помощь
Если возврат неверно учитывает бонусы, я разберу модель заказа, распределение скидок, платежный refund и bonus ledger, исправлю формулу и помогу безопасно сверить старые операции.