Disputes API — это сервис на Java (Spring Boot), предназначенный для работы со спорами (disputes) в финансовой инфраструктуре. Основные задачи:
- Создание, хранение, обновление и завершение (закрытие) споров;
- Взаимодействие с внешними сервисами (такими как PartyManagement, Invoicing, FileStorage, Dominant, TgBot, ProviderPayments и др.) для получения/отправки данных или выполнения операций в рамках процесса урегулирования споров;
- Обработка уведомлений от внешних систем, а также отправка callback-уведомлений;
- Плановое (scheduled) выполнение задач, связанных со статусами споров, их обработкой, оповещениями и т.д.;
- Безопасность и авторизация через сервис Bouncer (или TokenKeeper), а также конфигурация доступа.
Ниже описан полный жизненный цикл спора по финансовой транзакции
-
Инициирование спора
- Отправная точка: кто-то (чаще всего мерчант или покупатель, действующий через мерчанта) решает оспорить платёж. Причин может быть множество: покупатель недоволен качеством товара, не узнаёт транзакцию, считает, что деньги списали ошибочно и т.д.
- Проверка возможности спора: система смотрит, не вышли ли сроки давности, действительно ли существует такая оплата, не было ли уже открыто слишком много споров по этому же платежу. Если всё валидно, спор считается «принятым» к рассмотрению. В противном случае система откажет или известит, что спор нельзя открыть.
- Запись в систему: создаётся «карточка» спора — системная запись с информацией о платеже, сторонах, сумме, возможных причинах и т.д. Все изменения по этому спору будут фиксироваться здесь же.
-
Предварительная проверка
- Внутренний анализ: сразу после создания система может проверить статус платежа (например, не отменён ли он уже, не возвращены ли средства), сверить данные об отправителе и получателе, убедиться, что оплата прошла по корректным реквизитам.
- Уведомление сторон: система сообщает ключевым участникам (часто это сам мерчант, платёжный провайдер, иногда — административный персонал), что спор открыт. Если нужно, рассылаются письма, push-уведомления или другие сигналы.
-
Сбор доказательств и материалов
- Загрузка файлов: могут понадобиться копии чеков, договоров, переписки. Участники спора (мерчант, администратор) передают их системе, которая хранит всё это, чтобы в дальнейшем при рассмотрении был доступ к документам.
- Проверка прав: система разрешит загружать или просматривать документы только тем, у кого достаточно полномочий. Например, мерчант видит все файлы по своему магазину, администратор — по любому спору, а третьи лица — нет.
-
Изменение статуса по ходу рассмотрения
- Промежуточные статусы: спор может долго находиться «в ожидании» ответа от внешнего провайдера, решения мерчанта или покупателя. В этот период система регулярно проверяет, не пришло ли новое событие (например, «провайдер подтвердил транзакцию» или «покупатель отозвал претензию»).
- Автоматические триггеры: если долго нет ответа, система может автоматически изменить статус: например, «срок ответа истёк — спор закрыт в пользу мерчанта» или «провайдер не подтвердил операцию, средства возвращены».
- Вмешательство администратора: если возникают нештатные ситуации (спор «завис» или есть противоречивые данные), администратор вручную меняет статус или даёт указание: «Вернуть деньги», «Отказать в возврате», «Продлить время ожидания» и т. п.
-
Взаимодействие с внешними системами (платёжные провайдеры и т.д.)
- Входящие уведомления: провайдер может прислать «обратный вызов», сообщив о принятом решении (например, банк подтвердил транзакцию). Система обработает этот сигнал и обновит спор: «претензия отклонена», «деньги зарезервированы для возврата» и т. п.
- Исходящие уведомления: при каждом ключевом событии (начало спора, его завершение, возврат средств) система отправит сигналы тем, кто должен знать (другим сервисам, боту поддержки, дополнительным каналам).
- Повторные попытки: если уведомление не доставлено (например, проблемы с сетью), система запланирует новые попытки через некоторое время. Аналогично и провайдер может повторно присылать уведомления, если не получил ожидаемый ответ.
-
Плановые проверки и автоматизация
- Регулярные задачи: чтобы никто не «забыл» спор, система периодически просматривает все споры и смотрит, нет ли затянувшихся статусов. Если обнаружит, что спор «висит» без ответа, может автоматически завершить его по заранее заданным правилам или повторно отправить запрос провайдеру.
- Напоминания: если у спора есть крайний срок, по истечении которого надо принимать решение, система может уведомить администратора или мерчанта, что «время вышло, нужно либо предоставить доказательства, либо будет автоматический отказ».
-
Завершение спора
- Успешное разрешение: после рассмотрения всех аргументов и доказательств принимается окончательное решение: вернуть средства, отказать в возврате и т. д. Система переводит спор в финальный статус, где уже ничего изменить нельзя.
- Денежные операции: если результатом спора стал возврат (частичный или полный), может быть инициирован процесс возврата через платёжного провайдера. Система зафиксирует, что средства отправлены обратно, закроет спор и уведомит участников.
- Отказ в удовлетворении: если спор признан несостоятельным, система тоже закрывает его, помечает как «завершён» с результатом «отказано», уведомляет стороны и прекращает дальнейшие действия.
-
Специальные случаи и ошибки
- Конфликт статусов: бывает, что пока одна сторона обновляет спор, другая успевает изменить его в другом процессе. Система может обнаружить расхождение и либо выдать ошибку, либо синхронизировать данные по определённым правилам.
- Непредвиденные ситуации: если при проверке выяснится, что платеж уже давно отменён или не существовал, система может отклонить открытие спора или досрочно его завершить.
- Истёкшие сроки: если спор слишком «старый», система откажет в рассмотрении с пояснением, что срок давности для претензий уже прошёл.
- Проблемы доставки: если уведомления или запросы к провайдеру по какой-то причине недоступны, система записывает это и продолжает пытаться доставить информацию в фоновом режиме, пока не сработает лимит попыток или не восстановится связь.
Таким образом, сервис «ведёт» спор по жизненному циклу: от момента, когда кто-то решает, что с оплатой что-то не так, до окончательного решения о том, возвращать ли деньги и кому. При этом система старается учесть все возможные сценарии — от успешного автоматического завершения до форсированных действий администратора или автоотказа при истечении срока. Все изменения в споре журналируются и при необходимости могут быть пересмотрены, а доступ к управлению и просмотру контролируется уровнем прав, чтобы никто случайно или намеренно не нарушил процедуру.
Ниже представлена сводка (обзор) структуры проекта и ключевых сценариев бизнес-логики. Документация предназначена для того, чтобы помочь в понимании основных модулей, сервисов и сценариев обработки споров (disputes) и взаимодействующих с ними компонентов.
Пакеты в src/main/java/dev/vality/disputes можно условно разделить на несколько крупных блоков:
- admin — функциональность, связанная с административными операциями, колбэками и управлением (например,
AdminManagementDisputesService
,CallbackNotifier
и т.п.). - api — публичные API-интерфейсы и делегаты (
DisputesApiDelegate
,DisputesApiDelegateService
), конвертеры моделей, а также сервисы, работающие с основным REST/HTTP-интерфейсом (например,ApiDisputesService
,ApiNotificationService
). - config — конфигурация Spring (бины, доступ, настройки кэширования, сетевых запросов, конфигурация http-клиентов).
- dao — доступ к базе данных (Data Access Objects) и модели для хранения (например,
DisputeDao
,FileMetaDao
,NotificationDao
). - exception — классические Java-исключения, связанные с нарушением логики: авторизация, не найденные сущности, неверный статус, итд.
- flow, merchant, polling, provider и schedule — модули, отвечающие за различные аспекты бизнес-процессов и интеграций (обработка статусов, callback-уведомлений, плановые задачи, интеграция с внешними провайдерами).
- security — модули, отвечающие за авторизацию и работу с контекстом Bouncer/TokenKeeper.
- service — основная логика (бизнес-сервисы) по обработке споров, взаимодействие с внешними системами (InvoicingService, PartyManagementService, FileStorageService и т.п.).
Ниже даётся обзор ключевых сценариев бизнес-логики, где наиболее активно задействованы указанные компоненты.
- Создание спора (merchant/admin, проверка статусов, запись в БД, уведомления).
- Обновление спора (переход статусов, логирование, колбэки, уведомления).
- Административные операции (обзор/поиск споров, форсирование статусов, пересылка доказательств).
- Callback’и от внешних провайдеров (получение, валидация, апдейт статуса).
- Отправка внутренних callback’ов/уведомлений (TgBot, другие системы).
- Плановые задачи (отслеживание “забытых”/ожидающих споров, пересылка неудавшихся уведомлений и пр.).
- Работа с файлами (FileStorageService, DAO для метаданных).
- Безопасность (Bouncer/TokenKeeper, проверка прав, конфиг).
- MerchantDisputesHandler (пакет
dev.vality.disputes.merchant
) обрабатывает входящие запросы от мерчанта (черезMerchantServlet
). - CreateRequestConverter (там же, в
converter
) преобразует входной DTO/JSON в модель, понятную системе. - DisputesService (или
ApiDisputesService
для REST-уровня) в пакетеservice
производит дальнейшую обработку, создаёт запись о споре в базе (DisputeDao
). - В процессе создаётся запись о споре в БД, возможна валидация статуса платежа (через
InvoicingService
,PaymentStatusValidator
). - Запускаются дополнительные проверки или уведомления внешним сервисам (в частности, через
BouncerService
могут проверяться права, а черезTgBotService
может уйти сообщение-уведомление).
Итого: Сценарий “Создание” фокусируется на принятии параметров спора, валидации необходимых полей (статус платежа, возможность спора), записи в БД и вызове callback’ов (при необходимости).
- Логика изменения статуса (например,
ApiDisputesService
илиAdminManagementDisputesService
) вызывает метод изDisputesService
. DisputesService
проверяет текущий статус спора, сверяет его со статусом, поступившим от внешнего сервиса (или админской панели), а также осуществляет валидацию перехода.- Далее через DAO-слой обновляется статус и пишется в лог (а при некоторых переходах — пересчитываются связанные данные).
- При особых случаях (например, если спор выигран/проигран) может вызываться вызов во внешние системы (уведомления
ApiNotificationService
, callback вprovider.payments.callback.ProviderPaymentsCallbackHandler
и т.п.). - Параллельно могут быть запущены плановые процессы (
schedule
), которые проверяют “забытые” (зависшие) споры (ForgottenDisputesTask
) либо ждут завершения спора (PendingDisputesTask
).
Сценарий “Обновление статуса” часто инициируется либо внешней системой (callback), либо администратором, либо при исполнении плановой задачи.
- AdminManagementHandler (в
admin.management
) и AdminManagementDisputesService обеспечивают доступ к операциям управления для администраторов. - Доступ к этим методам контролируется через настройки безопасности (
AccessConfig
,BouncerServiceImpl
). - Администратор может:
- Просматривать споры (фильтрация/поиск через
DisputeDao
); - Менять статус спора (например, переводить в “REFUNDED” или “REJECTED” и т.п.), что ведёт к логике из п. 2.2;
- Вызывать переотправку уведомлений, создавать комментарии или загружать дополнительные файлы/доказательства (через
FileStorageService
).
- Просматривать споры (фильтрация/поиск через
- Пакет
provider.payments.callback
(ProviderPaymentsCallbackHandler
,ProviderPaymentsCallbackServlet
) занимается приёмом уведомлений от внешних провайдеров. - При приходе callback’а система валидирует соответствие (уникальность, статус, отсутствие дубликатов). Если успешно — обновляет статус спора (или платежа в контексте спора), записывает обновлённую информацию (DAO-слой, например
ProviderCallbackDao
), после чего может инициировать дальнейшие действия (запуск chain’ов, нотификаций, обновление invoice-состояния черезInvoicingService
). - В случае конфликтов (дублирующие callback’и, конфликт статусов) выбрасываются исключения (
ProviderCallbackAlreadyExistException
,ProviderPaymentsUnexpectedPaymentStatus
и т.п.) и callback может быть обработан повторно или отклонён.
- CallbackNotifier (и его реализации
DisputesTgBotCallbackNotifierImpl
,DummyCallbackNotifierImpl
) в пакетеadmin.callback
. - При изменении статуса спора (или иных событиях) Disputes API может отправить уведомление (например, в Telegram-бот или в другой внешний сервис).
- Конфигурация, куда отправлять, задаётся в
TgBotConfig
и др. - Либо могут быть вызваны методы
ApiNotificationService
, которое, в свою очередь, создаёт запись вNotificationDao
и при успешной отправке обновляет статус уведомления. - Параллельно существуют задачи из пакета
schedule
(например,NotificationTask
), которые периодически проверяют, есть ли необработанные/неотправленные уведомления, и пытаются их доставить повторно.
В пакете schedule
сосредоточены регулярные задачи, связанные с обработкой споров и нотификаций:
- CreatedDisputesTask: обрабатывает недавно созданные споры (например, для дополнительных проверок, триггеров).
- PendingDisputesTask: выявляет “зависшие” споры, где ожидается действие от провайдера/внешней системы, и в случае истечения таймеров (конфигурируются в
DisputesTimerProperties
) обновляет статус или делает повторные запросы. - ForgottenDisputesTask: обрабатывает споры, у которых сильно просрочен SLA (например, ожидается ответ, а ответа долго нет).
- NotificationTask: пытается повторно отправить уведомления, которые не были доставлены.
- Задачи используют сервисный слой (например,
CreatedDisputesService
,PendingDisputesService
,NotificationService
) и, при необходимости, взаимодействуют с DAO (для получения списка нужных споров/уведомлений) и затем обновляют их статусы.
- В бизнес-процессах по спорам часто требуется загрузка дополнительных материалов (доказательств) — чеки, скриншоты, договоры и т.д.
- В пакете
service.external
естьFileStorageService
(иFileStorageServiceImpl
), которое через REST/gRPC или иной протокол обращается к внешнему File Storage сервису. - В DAO-слое есть
FileMetaDao
, позволяющий хранить метаданные о файлах, связанных со спором. - При загрузке файлов сервис проверяет права пользователя, валидирует формат, сохраняет данные, возвращает ссылку (URL) на скачивание или предпросмотр.
- При разрешении спора администратор или мерчант могут прикреплять/просматривать файлы (логика в
ApiAttachmentsService
).
- Пакет
security
содержит логику работы с Bouncer (или TokenKeeper). AccessService
,BouncerService
иTokenKeeperService
проверяют, имеет ли субъект (пользователь, мерчант, админ) право выполнять данную операцию (например, создавать спор, менять статус, смотреть чужие споры).AccessConfig
иBouncerProperties
могут содержать настройки эндпоинтов, ключей, секретов и т.д.ContextFragmentName
показывает, какие именно части контекста проверяются: инвойсы, шопы, операции над платежами и т.д.