Skip to content

Latest commit

 

History

History
156 lines (112 loc) · 20.8 KB

File metadata and controls

156 lines (112 loc) · 20.8 KB

disputes-api

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

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

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

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

Модуль API

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

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

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

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

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

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

Модуль Schedule

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

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

Не все провайдеры на данный момент поддерживают работу с диспутами по 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 , то тоже отправляет на ручной разбор

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

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

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

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

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

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

промт

хочу написать документацию по этому сервису https://github.com/valitydev/disputes-api , нужна твоя помощь. сначало проанализируй репозиторий с исходним кодом сервиса, далее учти информацию из https://github.com/valitydev/disputes-api/blob/master/README.md (учти, что не вся информация из README.md актуальна. в случае не соотвествия или конфликта не используй информацию из README.md )и затем к полученной информации примени текст , который я написал далее . сделай поэтапно, чтобы не запутаться. используй лучшие практики написания документации исходного кода, не ориентируйся на мой стиль изложения, используй только саму информацию , а способ ее изложения и структуру подбери сама как самый подходящий и актуальный

есть несколько протоколов , которые реализует данный сервис

  1. https://github.com/valitydev/disputes-proto
  2. https://github.com/valitydev/swag-disputes

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

жизненный цикл диспута

  1. начинается с этапа его создания. диспуты создают мерчанты , присылают данные о платеже и чек в виде картинки . технически создать диспут можно либо через THRIFT использовав MerchantDisputesService$CreateDispute из https://github.com/valitydev/disputes-proto/blob/master/proto/merchant_disputes.thrift#L9 либо через RESP API использовав /create из https://github.com/valitydev/swag-disputes/blob/master/openapi/openapi.yaml#L35. создание диспутов через THRIFT использует телеграм бот, код которого описан здесь https://github.com/valitydev/disputes-tg-bot . далее сконцетрируемся только на работе через THRIFT
  2. далее информация о диспуте сохраняется в базе данных postgresql , картинка сохраняется в сервис https://github.com/valitydev/file-storage
  3. далее шедулатор, ориентируясь на время из dspt.dispute.next_check_after достает диспуты по расписанию и сделав некоторые предварительные проверки (например - статус платежа в системе через запрос в Invoicing$GetPayment из https://github.com/valitydev/damsel/blob/master/proto/payment_processing.thrift#L1117 и статус платежа в провайдере через запрос в ProviderPaymentsService$CheckPaymentStatus из https://github.com/valitydev/disputes-proto/blob/master/proto/provider_payments.thrift#L12 , который реализуют адаптеры, которые являются интеграцией для подключения к провайдеру) посылает запрос к провайдеру (технически в адаптер , который его реализовывает) в ProviderDisputesService$CreateDispute из https://github.com/valitydev/disputes-proto/blob/master/proto/provider_disputes.thrift#L10 . получает ответ, в удачном кейсе вернется DisputeCreatedSuccessResult - это значит провайдер смог успешно создать диспут на своей стороне. в конце этого флоу происходит смена статуса на pending, чтобы следующий шедулатор смог начать пулить статус диспута
  4. далее на этапе проверки статуса происходит флоу похожий на пункт 3) . сервис посылает запрос к провайдеру в ProviderDisputesService$CheckDisputeStatus из https://github.com/valitydev/disputes-proto/blob/master/proto/provider_disputes.thrift#L12 . получает ответ, в удачном кейсе вернется DisputeStatusSuccessResult - это значит провайдер перевел диспут в успех на своей стороне. в конце этого флоу происходит смена статуса на create_adjustment - на этом статусе диспут ждет создание корректировки подсистемой ProviderPayments , далее эта подсистема после проведения корректировки переводит статус диспута в финальный.

это было описание основного флоу жизненного цикла диспута. также есть разные фичи и доп.возможности в этом сервисе. рассмотрим их:

  • мерчант может пулить статус диспута через https://github.com/valitydev/disputes-proto/blob/master/proto/merchant_disputes.thrift#L13 (этим занимается телеграм бот disputes-tg-bot )
  • имеется реализация подсистемы для работы с платежами и проведением корректировок ProviderPayments
  • подсистема для работы с платежами и проведением корректировок ProviderPayments может автономно проводить корректировки, даже если диспута не существует. это происходит, когда адаптер присылает колбек используя функцию ProviderPaymentsCallbackService$CreateAdjustmentWhenFailedPaymentSuccess из https://github.com/valitydev/disputes-proto/blob/master/proto/provider_payments.thrift#L8 . подсистема проверяет статус платежа в адаптере и далее , если платеж у провайдера в успехе, а в нашей системе в фейле, меняет сохраняет информацию в базу данных в dspt.provider_callback. далее шедулатор подсистемы ProviderPayments вытаскивает платеж и делает корректировку
  • имеется реализация функционала AdminManagementService из https://github.com/valitydev/disputes-proto/blob/master/proto/admin_management.thrift. этот функционал используют саппорты для управлением диспутом через телеграм бота disputes-tg-bot
  • имеется отдельный шедулатор NotificationTask, который отправляет уведомления мерчантам по изменению финального статуса диспута. мерчант получает данные , модель которых описана здесь https://github.com/valitydev/swag-disputes/blob/master/openapi/openapi.yaml#L150

будет лучше если добавить несколько визуализаций в виде диаграм , блок схем или любого другого удобного для тебя (гпт) способа сообщи, если что то нужно уточнить