Студия разработки сайтов и приложений

Netspark.ru

Платформа для ботов в Telegram

Ботопотамы

Интеграция Доктуриста с Точкой

Турагенты могут вести десятки броней одновременно. Каждый день — новые оплаты от туристов, каждый день нужно вручную открыть банк, найти платёж, отметить в CRM оплату по туру (иначе как отслеживать что ничего не забыл?) Звучит как мелочь — но это копится ежедневно, сбивает с ритма и иногда порождает ошибки. И в целом это довольно нудно — переписывать руками из одного места в другое. А что нудно — нам делать не очень хочется.

Поэтому мы добавили в Доктурист возможность импорта платежей из банков и начали с интеграции API банка Точка. Здесь рассмотрим кейс о том, как подключили Точку к Доктуристу: сделали автоматические уведомления о платежах, умную привязку к бронированиям и создание платёжных ссылок прямо со страницы брони. И о нескольких нетривиальных вещах, с которыми пришлось разобраться по дороге. Технических и не очень.

Дано

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

payments.png

Задача: нужно сделать что-то более автоматизированное, чем старательно переписывать входящие платежи из банк-клиента. То есть получать входящие платежи в Доктуристе.

Кроме того, агенты часто принимают оплату по ссылкам: создают в банковском приложении ссылку на оплату определенной суммы картой и/или СБП и отправляют туристу. Это такой облегченный эквайринг, когда можно не делать полноценную форму оплаты. И банк не против (если соблюсти определенные правила). Задача 2: почему бы не дать пользователям возможность создавать ссылки на оплату прямо из интерфейса Доктуриста. Чтобы они могли копировать их и отправлять клиентам, а банковское приложение открывать было не нужно.

Вот что конкретно мы научили Доктурист делать:

— получать уведомления от банка о каждом входящем платеже;
— автоматически привязывать платеж к бронированию (если это возможно);
— давать пользователю привязывать платеж к бронированию вручную (если автоматически не вышло);
— создавать ссылки на оплату картой и/или СБП.

С чем столкнулись в процессе

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

Вебхуки как подписанный JWT

Первое, с чем пришлось разобраться — формат входящих уведомлений от банка. В отличие от сервисов, которые присылают простой JSON с подписью в заголовке, Точка отправляет тело вебхука целиком как подписанный JWT. Это значит, что нельзя просто прочитать JSON — нужно сначала верифицировать подпись публичным ключом банка.

Контроллер читает сырое тело запроса и декодирует JWT через библиотеку firebase/php-jwt:

$token = $request->getContent();
$pem   = config('services.tochka.jwt_public_key_pem');
$data  = JWT::decode($token, new Key($pem, 'RS256'));

При любой ошибке декодирования — логируем и тем не менее отвечаем http 200. Это важно: Точка интерпретирует любой не-200 как сбой и будет повторно доставлять уведомление. А так — бизнес-логика не выполняется, но и банк не уходит в цикл отправки.

Тестирование этой механики потребовало отдельных настроек инфраструктуры: RSA-пара генерируется прямо в тестовом классе, публичный ключ подставляется в конфиг, и тогда мы подписываем тестовый пейлоад так же, как это делает Точка и расшифровываем как живое приложение.

Умная авто-линковка

Когда платёж приходит от банка, система пытается найти бронь автоматически — по тексту назначения платежа (например, «Оплата по бронированию ДЕМ-001»). Здесь возникает классическая проблема строкового поиска: если в агентстве есть брони ДЕМ-10 и ДЕМ-100, то поиск ДЕМ-10 найдёт обе. Но мы не хотим ошибиться при связи броней с платежами, поэтому автоматически связываем только если найден ровно 1 результат.

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

Очень умная авто-линковка

Чтобы пользователи не забывали включать в назначение платежа номер брони, сделали, чтобы дефолтный текст при генерации платежной ссылки уже включал номер. Но боевое использование показало, что почти все платежи проходят мимо и ни с чем не линкуются.

payments_bank.png

После некоторых исследований оказалось, что хотя платежи в банк-клиенте показываются с нормальным назначением типа Оплата тура ФИО, номер брони, как мы задумали, в вебхук почему-то вместо этого прилетает Оплата по QR-коду ID QRABCD12345678 …. Пришлось реализовать дополнительный метод из API, который при получении платежа с таким описанием дергает Точку еще раз, чтобы получить инфу о данном QR-коде. В этой информации назначение платежа уже правильно отражается и по нему можно линковать платеж к брони.

Валютная конвертация при привязке

Точка естественно присылает суммы в рублях, поскольку расчёты в России могут быть только рублевые. Но брони в Доктуристе могут быть заведены и в USD, и в EUR, так как туроператоры могут работать в этих единицах, а платежи принимать по курсу. Когда пользователь вручную привязывает рублёвый платёж к заявке в EUR, система должна рассчитать сумму в EUR, чтобы записать её в правильной валюте. При этом у каждого туроператора свой курс конвертации.

Для этих случаев сделали два сценария:

  1. Валютная заявка + override → пользователь вводит вручную, какое количество USD/EUR получается из платежа (например, «150 EUR»), мы используем это значение в сохраняемом платеже по брони и вместе с ним сохраняем расчетный курс валюты.
  2. Валютная заявка, без override → берём последний курс ЦБ (он обновляется каждый день). В таком случае мы отмечаем связанный платеж как требующий внимания, поскольку авторасчет по курсу ЦБ это примерный расчет, а курс у операторов всегда выше.

Дефолтные значения при создании платежных ссылок

В процессе использования интеграции оказалось, что во многих случаях пользователи хотят создавать платежные ссылки с одним и тем же пресетом, например «Карта выключена, СБП включен». Ошибка в выборе средства оплаты может привести к денежным потерям (комиссия банка на эквайринге и СБП очень разная), что конечно недопустимо.

payment_links.png

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

Песочница

В самом начале разработки из документации на API Точки выяснилось, что у них нет полноценной песочницы, позволяющей проводить «игрушечные» платежи, есть только фиксированный набор тестовых запросов, которые, если сделаны верно, возвращают фиксированные ответы. Тестовые запросы конечно небесполезны, но без песочницы невозможно, например, провести полный тестовый цикл: генерация платежной ссылки → её оплата → получение платежа → разбор и линковка к брони. Только по частям.

Собственно, создание ссылок пришлось по большей части тестировать наживую, поскольку оно работает только на аккаунтах, для которых уже активирован эквайринг, а для остальных аккаунтов кидает ошибку 403 "Forbidden by consent".

Но зато в банке есть линия поддержки по интеграции API, которая действительно отвечает по имейлу и даже дает подробную информацию, которой в документации не нашлось.

Результаты

100+ тестов на разные случаи жизни. Мы сделали верификацию JWT-подписи, дедупликацию, авто-линковку с граничными случаями, валютную конвертацию, права доступа. Интеграция API работает с Laravel Horizon, то есть все входящие и исходящие процессы (уведомления о платежах, создание платежной ссылки, регистрация пользователя в системе, регистрация вебхуков) запускаются асинхронно в очередях. А для уведомления ожидающих пользователей используются вебсокеты через Reverb.

Реализация позволила создать и протестировать логику для импорта платежей и создания платежных ссылок, которая не обязана привязываться к одному банку. Теперь необходимо добавить уровень абстракции между платежами/ссылками и API банка, и можно подключать другие банки к системе уже с меньшими трудозатратами. Следующий кандидат на интеграцию — Альфа.

Интеграция уже работает в продакшене. Агенты создают платёжные ссылки прямо со страницы брони и отправляют их туристам — без переключения в банковское приложение. Входящие платежи появляются в системе автоматически и в большинстве случаев сами привязываются к нужному бронированию. Руками нужно разбирать только то, что система не смогла распознать однозначно.

Обсуждение

Чтобы обсудить заметку, написать комментарий, или просто связаться, заходите в Телеграм-канал. У нас весело и всем рады!

Также меня можно найти в Хвиттере, VC.ru, Дзене, или Тенчате. А если вы на парковке, присоединяйтесь к каналу в Max!