Всем привет. Наш банк занимаемся не только финансами: есть куча проектов в других областях. Например, транспортный проект — GorodPay. Он стартовал лет 8 назад как бизнесовое решение для перевозчиков, а теперь вышел за рамки B2C.
Мы сделали так, что в общественном транспорте Санкт-Петербурга, Екатеринбурга, Воронежа и ещё примерно 30 городов России можно не только купить билет за наличку или проехать по проездному, но и расплатиться банковской картой.
Мне стало интересно, как работает транспортный процессинг, как терминалы обрабатывают транзакции, какие проблемы пришлось решать, скрещивая ежа с ужом банк с общественным транспортом, и я пошел задавать вопросы команде GorodPay. Собрал их ответы под катом.
B2B, B2G… B2C?
В 2014 году Газпромбанк начал развивать транспортные проекты. Сначала это были технические решения для администраций городов и перевозчиков-юрлиц. У каждого пассажира была страничка с его данными — нечто вроде личного кабинета. Впрочем, самому пассажиру доступ к своим данным не давали — казалось, что незачем.
Появилась проблема. На одного пассажира заводилось несколько «личных кабинетов». Дело в том, что мы идентифицировали пассажира по номеру карты. А если он платил в транспорте разными картами, то и кабинетов у него было столько же — по одному на карту.
Впрочем, несколько кабинетов у одного человека — не так страшно (он ведь их даже не видел). Была проблема посерьёзнее.
Вот у пассажира закончились деньги на карте. Карта попала на транспорте в стоп-лист. Пассажир пополняет счёт — а оплатить всё равно нельзя, карта-то в стоп-листе. И как её оттуда убрать?
Тогда решили собрать мобильное приложение для пассажиров. Условия поставили такие:
идентифицировать человека по номеру телефона;
давать возможность привязать все банковские карты, которыми он пользуется в транспорте;
уведомлять, что карта попала в стоп-лист, и позволять вывести её оттуда;
К сожалению, в пункте выше оказался нерешаемый нюанс. Стоп-лист обновляется не постоянно, а с интервалом — от 20 минут до суток, зависит от связи оборудования с интернетом. Поэтому, когда пользователь выводит карту из стоп-листа, она начинает работать не мгновенно — со следующего обновления.
позволять просматривать поездки;
давать печатать билеты и чеки — например, для командированных;
разрешать добавлять в приложение городскую транспортную карту, пополнять её легко и без комиссии.
Работающий MVP собрали за три месяца. Ещё через два месяца он стабильно функционировал в проде.
Технические подробности: для тех, кому интересно, что под капотом
Транспортный предпроцессинг (ТПС) на Oracle, платёжная система — СПЭК
Еще есть плгоритм сквозной идентификации карты между ТПС, СПЭК и будущими партнерами. Например: сервис «Транспорт Онлайн» — платформа собирает координаты с любых провайдеров и выдаёт информацию в мобильное приложение. А оператор «Сеть партнёрств» и его подписка «Огонь» выпускают виртуальные карты в приложении
Предпроцессинг
ТПС (TPS, Transaction Processing System, система обработки платежей) позволяет пассажирам заходить в транспорт быстрее. Не нужно ждать, пока завершится платёж. Платежи копятся в терминале и авторизуются пачками — так называемая отложенная авторизация. Когда очередная пачка набирается, терминал отправляет транзакции в предпроцессинг.
Предпроцессинг очищает эти транзакции от транспортных данных и передаёт дальше — банку-эквайеру для расчётов.
Но если процессинг банка видит, что у клиента нет денег на карте? Пассажир-то уже на транспорте и с билетом.
Вот тогда ТПС и заносит карту в стоп-лист. А потом рассылает новые версии стоп-листа вместе с обновлениями для сети терминалов. И когда карту прикладывают к терминалу, первая проверка — карта в стоп-листе или нет. Так что ездить бесплатно не выйдет. По крайней мере, больше одного раза.
Получается, мы всегда рискуем стоимостью одной поездки на транспорте — буквально выдаём её каждому пассажиру в кредит на короткое время. Зато транзакции обрабатываются в разы быстрее. Если же для каждого пассажира проводить транзакцию онлайн, риск исчезнет — но в транспорт невозможно будет зайти из-за очередей.
Проездные
В 2014–2015 годах банковскую карту нельзя было использовать как проездной. Вообще. Сейчас каждую платёжную карту можно использовать для проезда. Причём технически доступны любые условия: количество поездок или скидка от полного тарифа; переносить остатки на следующий период или обнулять. После стоп-листа терминал проверяет, есть ли на карте поездки или социальные скидки. Если есть — карта списывает либо поездку, либо деньги.
Была ещё такая техническая история. У нас система насыщена интеграциями: и с банком, и с предпроцессингом, и с электронной коммерцией. Соответственно, в приложении мы храним банковские карты.
Карту добавить мы можем. А вот чтобы получить информацию о ней, нужен идентификатор. Идентификатор хранить мы тоже не можем: не имеем права по PCI DSS. То есть нам надо где-то получать идентификатор для каждого запроса. И он притом должен быть понятен всем нашим сервисам.
Потому сделали механизм обмена информацией (номер карты без личных данных) по типу токенизации. Так можно интегрировать другие сервисы, оставаясь в безопасности.
Организационно-архитектурный челлендж
Мы получали статус оператора персональных данных. Архитектура приложения подразумевала, что персональные данные будут храниться в отдельной изолированной службе.
С юридической стороны нужно, чтобы сервис, сбор и обработка данных соответствовали 152-ФЗ. Архитектура должна защищать данные. Мы создали микросервисную архитектуру, она не тормозит и охраняет данные даже при нагрузке в 100 тысяч пользователей в приложении одновременно.
Ещё важно организовать взаимодействие с ТПС. Он каждые 5–10 минут формирует набор изменений, которые произошли с предыдущей синхронизации, и на своей стороне весь набор данных кладёт в очередь RabbitMQ.
Мы забираем этот пакет и складываем в аналитическую базу данных ClickHouse от «Яндекса». Дамп базы Oracle размером 140–160 гигабайт на выходе в ClickHouse ужимается до 30 ГБ.
К тому же база аналитическая: оптимизирована для аналитики на больших объёмах данных. Среднее время запроса у нас сейчас — 40 мс.
Мы построили таблицы так, чтобы они партиционировались автоматически по нашим типовым запросам — по периоду времени и по карте. Для нас это входные данные. Бо́льшая часть запросов основана на них. Построив по ним партиции и раздробив весь массив данных на такие участки, мы очень быстро делаем агрегатные запросы по огромному массиву (сейчас там 966 млн записей).
Добавили кеширование, чтобы не ходить в базу постоянно. Наш умный фреймворк позволяет автоматически обновлять данные, когда это нужно, и кешировать ровно столько, сколько нужно. Мы показываем информацию, не тратя на это времени. При росте числа пользователей это будет сказываться исключительно положительно.
Цимус
Самое крутое решение, которое позволяет нам экономить мощности и одновременно обслуживать множество клиентов, — это умный кеш на ответы API. Мы реализовали в распределённом приложении систему управления кешем, основанную на распределённых событиях.
Выше уже говорили, что наше приложение — набор сервисов. Каждый из них выполняет свою задачу. Кешировать данные в отдельном сервисе проблемно, так как данные в других сервисах устаревают, следовательно, весь результат портится.
Первое, что мы реализовали, — механизм передачи событий между сервисами. Это не сложнее, чем нативные события в .NET. После мы научили кеш понимать, какие изменения могут испортить данные. Сейчас мы просто инвалидируем кеш. В будущем планируем пересчитывать по необходимости. Тогда горячие данные всегда будут в кеше.
Чтобы понимать, насколько это эффективно: некоторые запросы мы считаем за 1,5 с, а отдаём из кеша за 0,5 мс. В 3000 раз быстрее. Попаданий в кеш по «тяжёлым» методам при этом > 80%.
Итоги
Всё, что мы хотели воплотить в MVP, сейчас работает. У пассажира есть личный кабинет, в нём может быть любое количество карт: транспортных, банковских. Можно даже завести несколько личных кабинетов.
Всегда легко просмотреть историю — она сохраняется. Из стоп-листа выйти просто, к тому же о грядущем попадании в него пользователю сообщают заранее.
Личный кабинет чаще всего открывают из мобильного приложения. Приложение похоже на транспортное или банковское. Фронт-часть — современная, лаконичная. Минималистичный дизайн, логичный-понятный интерфейс и привычные уже всем сторис с основными фичами приложения и лайфхаками.
Управляем всем этим из админки. В ней несколько уровней и ролей доступов в зависимости от задач: для технических админов, менеджеров и специалистов клиентской поддержки.
Все обращения пользователя с сайта, из приложения и мессенджеров мы бережно храним. По номеру телефона можем поднять историю обращений и работу с ними.
Есть админка и для маркетологов, через которую создают, редактируют и публикуют всё те же сторис.
Стек
.NET Core 5 — на нём написано приложение. Планируем переехать на Core 6
RabbitMQ
Redis
gRPC
В архитектуру решения заложено горизонтальное масштабирование. Сервисы при перегрузках могут автоматически масштабироваться как вверх, так и вниз. Железо подобрано с запасом: чтобы проект мог расти без апгрейдов как минимум пару лет.
Планы
Раньше мы лишь обрастали интеграциями, теперь — расширяем покрытие. Но и интеграций станет больше. Кроме СПЭК для оплаты будут работать СБП, мобильные операторы, электронные деньги и так далее. А к ЕКАРТЕ и «Подорожнику» подключатся другие транспортные карты.
Разрабатываем программу лояльности GorodPay: например, каждая пятая поездка бесплатно.
Хотим дать платить за транспорт по QR-коду. Кстати, для других услуг код тоже сработает, не только для транспорта. Есть мысль развить идею в сторону NFC-меток — тогда пользователю не придётся искать QR в автобусе.
Грандиознейший из планов — технология, которая уберёт майферные истории, банковский предпроцессинг и переведёт всё на ЭДС. Просто формируем учётную запись в облаке и к ней создаём электронный кошелёк. Привязываем к этому кошельку различные идентификаторы: хоть брелок от подъезда, хоть NFC-метку, хоть Apple Watch. Если в чём-то есть NFC — можно будет из этого сделать идентификатор кошелька.
А в транспортном средстве будет валидатор с белым списком идентификаторов. Когда у пассажира положительный баланс на кошельке, тогда идентификатор в белом списке и клиент может оплатить этим идентификатором проезд, не имея при себе ни банковской карты, ни даже смартфона.
На этом всё. Спасибо за внимание! Если у вас есть идеи или вопросы — с интересом послушаем (то есть почитаем) в комментариях.