Refunds — это не кнопка, а отдельный круг ада в бизнес-логике
Возврат почти никогда не равен исходному платежу. Платёж прошёл через авторизацию, capture, сеттлмент, холды, комиссии и, возможно, частичный возврат. А потом кто-то в продукте решает сделать «простую кнопку вернуть деньги». И начинается: у одного ордер закрыт, у другого уже частичный refund, у третьего исходная транзакция потеряна в зоопарке статусов.
Нормальная схема возвратов держится не на UI, а на правилах:
— refund всегда идемпотентный, иначе получите двойные списания и двойные возвраты;
— храните связь refund ↔ payment ↔ order, а не «где-то в логах есть paymentId»;
— разделяйте full, partial и repeated refund, иначе бухгалтерия вам устроит экзорцизм;
— статус возврата должен жить своей жизнью, потому что у провайдера он часто «pending» дольше, чем у вас терпение. Документация — это ложь, логи — истина.
Ещё одна классика: деньги уже ушли на сеттлмент, а бизнес решил «откатить». Это не откат, это отдельная финансовая операция с собственным временем жизни. Поэтому возврат нельзя лепить как mirror payment. У него свой workflow, свои webhook'и, свои ретраи и свой dead-letter, если провайдер любит терять подтверждения.
Если у вас refund не покрыт тестами на частичный возврат, повторный клик, потерянный webhook и рассинхрон статусов — у вас не процесс, а костыль на костыле и финтехом погоняет. Идемпотентность или смерть.
Интеграция платежных решений
@payment_integration_ops_arb
Refunds — это не кнопка, а отдельный круг ада в бизнес-логике
Этот пост опубликован в Telegram-канале Интеграция платежных решений. Подписаться можно по ссылке: @payment_integration_ops_arb.