disputes-api/README.md
Anatolii Karlov df25417b6b
add disputes-tg-bot usage (#23)
* rename AdminManagementServlet

* add disputes-tg-bot usage

* add dummy for tg bot usages

* add mapping

* remove texeption catcher

* bumps deps, remove debug endpoint

* rename

* rename

* disable isScheduleReadyForCreateAdjustmentsEnabled

* fix tests

* up pg versino

* refactor ProviderRouting

* add handleUnexpectedResultMapping tests cases

* checkstyle

* maven-site issue

* bump workflow

* bump workflow

* bump workflow

* review fixes
2024-10-29 12:49:03 +02:00

13 KiB
Raw Permalink Blame History

disputes-api

Технические особенности

Сервисы разделены по модулям, к которым относится данный функционал, будь то ../api/, ../security/ или ../schedule/

Поэтому возможны смежные названия сервисов (как **AttachmentsService) , но при этом сервисы работают для разных задач, ограниченные рамками модулей

Реализовывать отдельное оповещение нет необходимости, потому что закладываемся, что мерчант настроит получение вебхуков по ивенту корректировки и webhook-dispatcher сам узнает об этом ивенте о пошлет уведомление

Модуль API

Модуль внешнего доступа для создания диспутов и опроса статуса. Все действия проходят через БД, проактивного запроса к провайдерскому API не происходит. Создание диспута означает лишь создание записи в БД, решение далее принимается на уровне модуля ../schedule/

Перед созданием нового диспута проверяем, существует ли уже какой либо открытый по данному платежу, потому что открытым может быть только один за раз, если существует, отдаем действующий

(реализации на данный момент нет) Передача uniqID на создании\статусе диспута дает возможность проверять наличие диспута (через опрос статуса) до этапа создания с внешнего вызова

При ошибках сервис кидает:

  • 5хх, если это отказ внешних шлюзов
  • 404, если это отсутствие данных
  • 400, если это ошибка в данных запросов
  • 401, если это отсутствие прав для доступа к API

При опросе диспута существует несколько значений параметра ErrorReason, при которых информация об диспуте не отдается и наружу выкидывается 404. Такое поведение возникнет, если в другом потоке шедулатора при обработке диспута возникли ошибки\отказ внешних шлюзов с ключевыми данными и в рамках данного диспута проблема является непреодолимой. Единственным способом решить данную проблему будет создание нового диспута.

Модуль Schedule

Модуль с шедулаторами, которые по расписанию обрабатывают диспуты.
Все процессы в шедулаторах проходят в изоляциях транзакций, при исключениях возникает откат.

При обработке диспутов шедулатор блочит используемые айдишники на уровне запроса к базе.
Диспут у провайдера не будет создан, пока платеж находится в не финальном статусе, это ограничение на уровне hellgate. Когда статус платежа становится captured|cancelled|failed разрешена дальнейшая обработка диспута.

Если при финальном статусе платежа captured создавать на провайдере диспут является не желательной ситуацией, можно установить опцию в терминале DISPUTE_FLOW_CAPTURED_BLOCKED и пулять состояние в топик\тг-провайдер-бот\filebeat на ручной разбор (AdminManagement module)

Не все провайдеры на данный момент поддерживают работу с диспутами по API.
Предполагается такой способ действия при этой ситуации:

  1. При наличии диспутов по API у провайдеров в терминале добавляется опция — DISPUTE_FLOW_PROVIDERS_API_EXIST
  2. если эта опция терминала есть, то сервис ищет роут до адаптера и отправляет запрос по трифт протоколу, иначе будет пулять в топик\тг-провайдер-бот\filebeat
  3. если возникнет ошибка роута, то будет пулять в топик\тг-провайдер-бот\filebeat

Используется экспоненциальный пуллинг, после которого мы считаем, что диспут протух и поллить его не надо. Опция DISPUTE_FLOW_MAX_TIME_POLLING_MIN контролирует максимальное время пуллинга
Диспуты на проверку статуса упорядочиваются по последнему времени проверки поля next_check_after, берутся самые древние

Ответственность за актуальный статус диспута несет конкретный адаптер. Решение остается за адаптером, тк детали реализации могут отличаться в зависимости от интеграции, disputes-api работает уже с результатом этого процесса и занимается обновлением статуса диспута в своей БД. При этом, при создании диспута необходимо в адаптере по возможности проверять наличие существующего диспута, в случае отказа внешних шлюзов после вызова ручки создания диспута на адаптере.

Если по какой то причине на этапе опроса статуса шедулатор не найдет в БД актуальную запись об конкретном provider_dispute_id (т.е. диспут в адаптере не был создан), то обработка диспута будет возвращена на этап назад, для попытки создать диспут в адаптере заново.

Перед созданием корректировки сервис пытается найти уже созданные корректировки и найти среди них существующую корректировку и при успехе переводит диспут в успех. Этот процесс ликвидирует ситуацию дублей корректировок.
В идеале, после создания корректировки диспут переводится в успех.

Консистентность

Данная реализация предусматривает, что сервис при обработке не зависнет в неопределенном состоянии, а если это произойдет, то это не повлияет на состояние данных

Критичные внешние шлююзы:

  • postgres [RW] (хранение данных по диспутам)
  • hellgate [RW] (запись корректировок)
  • file-storage-v2 [RW] (хранение файлов чеков диспутов)
  • dominant [R] bouncer [R] token-keeper-v2 [R] (авторизация по токену)

В модуле API при падении внешних узлов в общей цепочке вызовов будет откат транзакции, данные в БД не запишутся, диспут не будет создан и наружу вернется выбранный http-код

В модуле Schedule при падении внешних узлов в общей цепочке вызовов будет откат транзакции, данные в БД не запишутся, а также продусмотрена проверка hellgate , существует ли уже такая корректировка

Запись file-storage-v2 не регулируется на уровня транзакции сервиса, но конкретно в случае file-storage-v2 возможность повторной записи не является критичным фактором

Вызов адаптеров — пулинг на падения не влияет, но при создании предполагается, что это ответственность адаптера удостовериться, что такого диспута еще не существует.

Модуль ручного разбора

схема такая: после создания диспута через нашу внешнюю апишку, будет происходить несколько кейсов:

  • если CreatedDisputesService при попытке создать диспут в провайдере понимает, что такой ручки в провайдере не существут, отправляет на ручной разбор
  • если не выставлен флаг в опциях DISPUTE_FLOW_PROVIDERS_API_EXIST , то тоже отправляет на ручной разбор
  • если это captured платеж и выставлена опция DISPUTE_FLOW_CAPTURED_BLOCKED , то тоже отправляет на ручной разбор

Далее, через внутрений трифт-интерфейс саппорт получает способ манипулировать диспутом для его обработки (AdminManagementDisputesService)

  • Перед переводом диспута в финальный статус саппорт должен будет забиндить айди созданного диспута в провайдере через ручку BindCreated(). Здесь особенность, что этот метод фильтрует возможность биндить диспуты только созданные вручную (из manual_created)

Далее, в режиме ручного разбора есть опция финализации диспута в фейл (CancelPending()) либо в успех (ApprovePending()). Здесь особенность, что в фейл можно перевести любой диспут имеющий не финальный статус, а в успех можно перевести, только если гарантировано создан внешний диспут у провайдера ( из pending,manual_pending)

  • Из за того, что для ручных диспутов добавлены отдельные статусы manual_pending ,manual_created не происходит ситуации, что такие диспуты попадут в таску PendingDisputesService которая автоматически вызывает апи провайдера для проверки статуса

Схема аппрува корректировок

Добавить в БД для диспута колонку "IS_AUTO", по дефолту проставляем туда FALSE. Получаем успешный статус от провайдера, пишем в БД статус READY_FOR_ADJUSTMENT. Корректировки проводим в отдельном потоке по расписанию, как сейчас создаем и проводим диспуты (просто последний вариант разбиваем на два отдельных). По расписанию стартует поток и автоматически проводит только те проводки, где IS_AUTO = TRUE. В идеале саппорт/фины будут скриптом/в админке сначала проставлять TRUE.