Тема
Баланс, комиссии, финализация
Финализация — момент, когда транзакция получает окончательный результат и система впервые трогает деньги мерчанта. До этого момента на счёте не меняется ничего. Общее устройство счёта и его сумм описано в модели денег; здесь — что именно происходит в момент завершения платежа и почему это нельзя повторить дважды.
Почему до финализации баланс не меняется
Пока транзакция в обработке, результат нам неизвестен: банк может подтвердить операцию, отказать или промолчать до таймаута. Любое предварительное зачисление пришлось бы откатывать, а откат денег — самая дорогая ошибка в процессинге. Поэтому все изменения счёта собраны в одном месте: в обработчике успеха и в обработчике отказа. Исключение одно — выплаты, где деньги наши собственные и проверить их наличие нужно до похода в банк; об этом ниже.
Расчёт комиссии
Комиссия считается от суммы операции и состоит из процентной и фиксированной части: сначала складываются обе, затем результат зажимается между нижней и верхней границами. Считается всё в целых минорных единицах, дробной арифметики в деньгах нет.
Ставка берётся не одна на всех. На счёте настроен базовый тариф, а рядом — условные тарифы, которые срабатывают по бренду карты, стране выпуска, группе стран или диапазону суммы. Кроме того, у тарифа есть история: если условия поменяли задним числом, для транзакции подбирается тариф, действовавший на момент её создания, а прошлые транзакции при необходимости пересчитываются.
Посчитанная комиссия — это то, что платит мерчант нам. Отдельно от неё записывается комиссия банка по настройкам гейта, а наш доход получается разностью и никогда не уходит в минус: так одна транзакция показывает отчётности и выручку, и себестоимость.
От комиссии зависят две производные суммы: сколько списывается с плательщика и сколько зачисляется мерчанту. Что из них равно исходной сумме заказа, определяет настройка счёта — комиссию можно удерживать из суммы мерчанта либо добавлять сверху к сумме плательщика.
Отказ тоже может стоить денег: если на счёте настроена комиссия за отклонённые операции, она считается и записывается на транзакцию. Исключение — внутренние ошибки: за них с мерчанта не берут ничего, и комиссия обнуляется явно.
Что происходит со счётом при успехе
Успешный платёж не зачисляется сразу в доступный баланс. Сумма за вычетом комиссии делится на две части: доля роллинга уходит в роллинг-баланс, остальное попадает в удержание. Тогда же ставится отложенная задача на снятие удержания — со сроком, заданным на счёте. Когда задача срабатывает, она уменьшает удержание и увеличивает доступный баланс, причём у основного счёта мерчанта в этой валюте, а не обязательно у того, на который пришёл платёж. Повторное срабатывание безопасно: задача помечает транзакцию и не снимает одно и то же удержание дважды.
Роллинг живёт по своему сроку: таймер удержания его не трогает, и в доступный баланс он возвращается отдельной операцией. Обратные по смыслу операции зеркальны: возврат и выплата при успехе уменьшают доступный баланс.
Аудит изменений баланса
Каждая финализация записывает отдельный документ: какая транзакция, в какой статус ушла и какие суммы на сколько изменились. Нужен он в двух случаях.
Первый — сверка. По этой записи любая цифра на счёте объясняется конкретной транзакцией, а не выводится из общих соображений.
Второй — принудительная перепроверка, которой пользуются вручную при разборе инцидентов. Перед тем как применить новый результат, система откатывает предыдущую запись, возвращая суммы на место, и только потом считает заново. Если по транзакции нашлось больше одной неоткатанной записи или её статус не тот, который ожидается, операция прерывается с ошибкой: это признак, что с транзакцией уже что-то делали руками.
Почему терминальные статусы не переигрывают
Из аудита следует главное правило этой механики. Обычная финализация обновляет транзакцию с проверкой текущего статуса: успех применяется только к транзакции в обработке, отказ — только к новой или обрабатываемой. Поэтому опоздавший ответ банка по уже закрытой транзакции не доходит до денежной части и ничего не удваивает. Снять эту проверку может только режим принудительной перепроверки, и именно он обязан сначала откатить прежнее изменение. Любая правка кода финализации должна сохранять оба свойства — см. Статусы и стадии.
Когда валюты не совпадают
Прямой расчёт сумм требует, чтобы валюта операции, валюта комиссии и валюта счёта совпадали, поэтому конвертация выполняется до него, а не внутри. Мест, где она возникает, три: приведение суммы платежа к валюте счёта, если это разрешено настройкой счёта и с оговорённой наценкой к курсу; перевод тарифа из валюты комиссии в валюту операции; пересчёт суммы и комиссии в валюту гейта, которая хранится на транзакции отдельно от суммы заказа.
Курсы система не придумывает: берётся последний записанный курс нужной пары от указанного поставщика. У курса есть срок годности, и если свежего нет, расчёт падает с ошибкой, а не идёт на устаревшем значении. Поставщик и курс остаются в записи, поэтому потом можно показать, почему конвертация прошла именно так.
Выплаты: деньги проверяются до банка
В выплате порядок обратный. При создании операции сумма резервируется на счёте: одной атомарной операцией она добавляется к выплатному удержанию при условии, что доступного хватает — то есть баланс покрывает новую сумму вместе с уже зарезервированным и разрешённым овердрафтом. Не хватило — транзакция сразу получает отказ с категорией нехватки средств, и в банк мы не идём.
При успешной финализации резерв снимается, а доступный баланс уменьшается на итоговую сумму. При отказе резерв просто возвращается, и счёт оказывается там, где был. Подробности сценария — в Выплатах.