Refunds — это не кнопка, а отдельный ад с идемпотентностью и сверками
Если у вас refund живёт как «минусовая транзакция» в той же логике, что и sale, поздравляю: вы уже строите костыль на костыле и финтехом погоняете. Возврат — это не отмена оплаты, а новая операция со своим жизненным циклом, статусами, частичными суммами и шансом отвалиться между провайдером, биллингом и бухгалтерией.
Нормальная схема держится на трёх правилах:
— idempotency key для каждого запроса на возврат, иначе дубли поймаете не в теории, а в проде;
— отдельный state machine: requested / pending / succeeded / failed / reversed;
— связка refund ↔ original payment ↔ settlement, потому что деньги могут уже уехать в сеттлмент, а бизнес всё равно решит «вернуть всё».
Главная ловушка — частичные возвраты и гонки. Один оператор нажал refund в админке, второй — в CRM, третий — прилетела повторная вебхук-обработка. Без жёсткой дедупликации и блокировки по платежу вы получите отрицательный баланс, двойной возврат или фантомный «успех» в интерфейсе. Документация — это ложь, логи — истина.
И да: статус у провайдера ещё не значит, что деньги уже дошли до карты. Для клиента возврат — ожидание, для вас — цепочка асинхронных событий, где любой таймаут надо уметь переиграть без ручного шаманства.
Запомните простое: refund-логика должна проектироваться отдельно от checkout. Идемпотентность или смерть.
Интеграция платежных решений
@payment_integration_ops_arb
Refunds — это не кнопка, а отдельный ад с идемпотентностью и сверками
Этот пост опубликован в Telegram-канале Интеграция платежных решений. Подписаться можно по ссылке: @payment_integration_ops_arb.