Всем привет. Наш банк занимаемся не только финансами: есть куча проектов в других областях. Например, транспортный проект — GorodPay. Он стартовал лет 8 назад как бизнесовое решение для перевозчиков, а теперь вышел за рамки B2C.  Мы сделали так, что в общественном транспорте Санкт-Петербурга, Екатеринбурга, Воронежа и ещё примерно 30 городов России можно не только купить билет за наличку или проехать по проездному, но и расплатиться банковской картой.   Мне стало интересно, как работает транспортный процессинг, как терминалы обрабатывают транзакции, какие проблемы пришлось решать, скрещивая ежа с ужом банк с общественным транспортом, и я пошел задавать вопросы команде GorodPay. Собрал их ответы под катом.  В 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 — можно будет из этого сделать идентификатор кошелька.  А в транспортном средстве будет валидатор с белым списком идентификаторов. Когда у пассажира положительный баланс на кошельке, тогда идентификатор в белом списке и клиент может оплатить этим идентификатором проезд, не имея при себе ни банковской карты, ни даже смартфона.  На этом всё. Спасибо за внимание! Если у вас есть идеи или вопросы — с интересом послушаем (то есть почитаем) в комментариях.