Страх, ненависть и токенизация банковских карт в Google Pay

Мы разработали фичу для восточноевропейского банка, благодаря которой клиент может подключить карту к Google Pay прямо в приложении. В проекте были задействованы пять участников: мы, банк, Google Pay, интегратор и токен сервис-провайдер. У трёх последних в документации были белые пятна, на прояснение которых ушло полтора месяца, хотя разработка с нашей стороны заняла всего две недели.

Я Владислав Кортиков, Android-разработчик в KODE. В статье рассказал, что может ждать вас при добавлении подобной фичи в банковское приложение. Здесь много неочевидных моментов, постигнутых с болью, и возможно однажды эта информация поможет кому-то сэкономить силы и время.

8fbf641b506e21272c1b3736e94045cd.jpg

Зачем банку токенизация

Банк хотел, чтобы клиенты могли добавлять карты в Google Pay прямо из мобильного приложения. Для этого ему нужно было токенизировать карты сначала на стороне сервера, а затем — в приложении. 

За год банк перевёл клиентов на карты VISA, а серверный вендор с лицензией от VISA токенизировал их на сервере. Оставалось сделать токенизацию карт в приложении. Летом 2023 года банк передал эту задачу нашей команде, которая имела подобный опыт и работала с сервисами Google.

С помощью фичи банк планировал увеличить мотивацию клиентов установить приложение и DAU — daily active users. Логика была такой:

  1. Банку нужно как можно больше клиентов в приложении, потому что там он предлагает продукты и услуги. 

  2. Клиенту удобно оплачивать покупки телефоном и не искать по карманам физическую карту.

  3. Чтобы оплачивать покупки телефоном, клиент может скачать приложение и прямо в нём добавить банковскую карту в Google Pay.

  4. А чтобы клиент мог добавить карту в Google Pay, банку нужно сделать фичу.

Три чёрных ящика

Чтобы разработать фичу, мне нужно было узнать о специальном API от Google Pay — Push Provisioning API. Информации в открытом доступе о нём не было. Мы связались с Google через менеджера банка и запросили документацию, которую я изучил вместе с аналитиком.

После чтения документации у нас остались три «чёрных ящика»:

  1. Google Pay,

  2. Интегратор,

  3. Токен сервис-провайдер.

В документации Android-разработчика не было точной информации о том, как работает сервер и как он общается с токен сервис-провайдером. Документация описывала, как всё должно быть на клиенте и давала подсказки для разных случаев. Но она не давала понимания, что сделал интегратор и точно ли он сделал это в соответствии с документацией токен сервис-провайдера.

Поэтому когда возникали проблемы, мне нужно было предполагать, что именно сделал не так интегратор или токен сервис-провайдер. Я заново открывал документацию, искал приписки внизу маленькими буквами и выдвигал гипотезу об изменениях. Если она не срабатывала, проверял следующую гипотезу. А когда упирался в тупик, шёл общаться к Google Pay, интегратору или токен сервис-провайдеру, и коммуникация затягивалась.

Основной сценарий по добавлению карты

Я поделил добавление карты на два этапа: сначала настроил каркас, а потом делал полноценное подключение.

Первый этап: интеграция с SDK Google Pay

Для взаимодействия с SDK Google Pay нужно было настроить интеграцию с Push Provisioning API. Я не писал логику и не соблюдал внутренние функциональные требования фичи, только следовал требованиям документации к API, чтобы сделать минимальную реализацию для проверки работоспособности. Затем я запросил у Google то, чего не хватило — например, разрешение на тестирование Push Provisioning API для конкретных версий приложения. Для этого нужно было заполнить анкету и описать конфигурацию приложения: передать Android ID и хэш ключа подписи. Этот этап помог сократить ошибки в будущем.

Также я написал надстройку над SDK, чтобы скрыть её внешние зависимости.

Второй этап: полная реализация сценария

После того, как я настроил работу интерфейсов, появился каркас для полноценного подключения. Мы собрали бизнес-требования из строгих требований Google к дизайну и требований банка, и приступили к работе.

Запрос Get OPC

У меня есть middleware-сервер — прослойка между сервисами банка и мобильного приложения. Чтобы добавить карту, нужно передать SDK специальный OPC-токен. Приложение не генерирует этот токен, и чтобы его получить, я отправляю на middleware-сервер запрос Get OPC. В запросе передаю серверу три параметра о пользователе:

  1. Идентификатор, который предоставляет SDK — Stable Hardware ID.

  2. Идентификатор текущего Wallet — кошелька, который установлен у пользователя.

  3. ID карты.

Сервер обращается к интегратору, тот общается с токен сервис-провайдером и возвращает обратно токен. При этом я не использую первые два идентификатора для сбора информации о пользователе.

Логика добавления карты

Пользователь выбирает карту, которую хочет подключить в Google Pay, и начинает сценарий добавления карты. Я получаю три идентификатора — Stable Hardware ID, идентификатор текущего Wallet и ID карты пользователя, вызываю метод Get OPC и получаю токен. Затем отдаю токен SDK и вызываю в нём метод Push Tokenize. 

Далее передаю управление SDK, и он открывает на своей стороне экраны вроде: «Вы хотите добавить карту в Google Pay. Подпишите необходимые соглашения». Пользователь остается внутри банковского приложения и проходит сценарий добавления карты: активирует её, подтверждает согласие на обработку данных. При успешном завершении сценария SDK возвращает результат в приложение и пользователь видит, что карта добавлена в Google Pay. После этого приложение может предложить ему назначить карту способом оплаты по умолчанию.

Если у пользователя на устройстве не одна активная учетная запись Google, а несколько, для каждой из них нужен свой Wallet ID. В таком случае нужно выбрать текущую активную учетную запись, которую установил пользователь, вызвать запрос Get OPC, отправить три идентификатора и получить токен.

Специальные сценарии

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

Показать пользователю в приложении, что он добавил карту в Google Pay

В SDK есть два способа показать пользователю, что карта добавлена:

  1. Метод ListTokens. По запросу он возвращает список токенов, которые принадлежат приложению — то есть список всех карт нашего банка. Данные других карт приложение не читает.

  2. Метод IsTokenized. По запросу метод возвращает true или false. Я задаю параметр в виде последних четырёх цифр карты, и когда метод возвращает true, понимаю, что для этой карты токен есть. Но если четыре цифры принадлежат карте другого банка, метод не сообщит об этом. Я буду думать, будто это карта нашего банка и у меня есть её токен. И не смогу проверить, своя она или чужая, потому что Google следит за сохранностью данных других банков.

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

Как решали проблему

Есть два вида окружения Google Pay — тестовое и релизное. Чтобы добавлять карты в тестовом режиме, нужно скачать на устройство специальный файл «android_pay_env_override_sandbox» по ссылке в документации и перезагрузить его. Тогда тестовое окружение Google Pay будет обращаться к тестовому окружению сервис-провайдера. 

Я сделал тестовую и релизную сборку приложения. Теперь нужно было сообщить в Google, что я умею работать с тестовым окружением и что тестовая карта нужна мне для проведения тестов, хоть её и не существует в реальности. Также нужно было сообщить токен сервис-провайдеру о том, что мне нужно поработать с тестовой картой — чтобы карта проходила реальный сценарий, но провайдер не считал её настоящей.

В итоге, когда что-то шло не по плану, мы разбирались с настройками токен сервис-провайдера, писали интегратору, а его сотрудники работали по скриптам и не горели желанием решить проблему. Помогло тщательное изучение технической документации. Когда токен сервис-провайдер или интегратор говорили, что на их стороне всё в порядке, у нас всё тоже было в порядке, но ничего не работало. Поэтому мы показывали им документацию Google и говорили «Смотрите, этот метод должен работать так, а у вас он работает по-другому». Так мы постепенно общались, проясняли «чёрные ящики» и в итоге получили токен, чтобы протестировать приложение и показать пользователю, что он добавил карту в Google Pay.

Тест-кейс с неоконченной ручной токенизацией карты

Также для добавления фичи нужно было пройти ряд обязательных тест-кейсов. Один из них — Yellow Path: ручное добавление карты в Google Pay через приложение Google. Он нужен на тот случай, если пользователь начал добавлять карту вручную, а не через приложение, и в момент, когда нужно пройти идентификацию, решил отменить добавление. Google ещё не активировал, но уже создал токен, а пользователь его ещё не подтвердил. Ручная токенизация нужна Google Pay, чтобы пользователь мог закончить добавление карты вручную.

В таком случае обычный способ «запустить Push Tokenize» не помогает. Он работает, только если токена ещё нет и нужно создать новый. Поэтому я проверял кейс методом ListTokens, который возвращает об этом токене следующие данные:

  • токен ID,

  • последние четыре цифры карты,

  • какому токен сервис-провайдеру она принадлежит: VISA или Mastercard,

  • состояние карты — активна ли она.

При проверке возникла проблема: ListTokens не возвращал эти данные.

Целиком путь данных выглядел так: я передавал данные, токен сервис-провайдер связывался с нашим бэкендом, затем с интегратором, а тот общался с ним и с Google Pay. В это время мобильное приложение общалось с SDK Google Paу. А я не на 100% знал, как работают три участника этого взаимодействия: Google Paу, токен сервис-провайдер и интегратор. Я точно знал только как работает middleware-сервер и часть с SDK — потому что SDK максимально описывает кейсы, которые могут произойти, хоть и не объясняет, как они согласуются с другими интеграциями.

Итак, нужно было решить проблему и получить данные с помощью метода ListTokens. Это заняло много времени, потому что все данные, которые у меня были, нужно было передать интегратору, который вычисляет токены и выдаёт их приложению. Оказалось, что токен сервис-провайдер что-то не до конца заполнил, и из-за этого Google не получал ID приложения, не мог понять, какие токены принадлежат нашему приложению, и отдать их нам с помощью метода ListTokens.

В итоге мы с командой общались с токен сервис-провайдером, который не всегда понимал, что мы хотим от него получить. Также мы общались с интегратором, который ожидал от нас ID и говорил, что мы его не присылаем. Мы передавали им ID приложения, по которому Google Pay выдал нам данные. Интегратор говорил, что формат неверный. Мы искали, исследовали и даже созванивались с токен сервис-провайдером и интегратором отдельно, чтобы понять, что за ID им нужен, на каком этапе и кто его теряет, и как нам его передать.

Мы понимали, что ID теряется не на нашей стороне — ведь у нас он есть. Требования Google сообщали, что если вы не получаете список, скорее всего, у вас неправильно указан какой-то параметр. Мы дублировали менеджерам параметр, который должен был получить интегратор. Начиналось обсуждение, тестирование, проверка — откуда здесь параметр, почему кто-то его не передаёт. 

Пришлось делать кучу предположений и проводить исследовательские работы. Например, я сделал все возможные версии приложения и согласовал их в Google с подписанными ключами, чтобы найти версию, которая сможет получить данные от ListTokens. Это было нужно, чтобы убедиться, что никто из «чёрных ящиков» не перепутал ID приложения, поскольку тестовое приложение отличается от релизного именно этим ID и ключом подписи. Для этого приходилось постоянно менять конфигурацию проекта. Каждая сборка занимала полчаса — очень много времени. Я проверил все версии, и ни одна из них не получала данные, поэтому я не мог получить список токенов от Google Pay. В итоге я обнаружил, что у интегратора есть собственная консоль разработчика, в которой есть ID для каждого созданного проекта. И интегратор просил не ID устройства, а ID проекта, который создается в их консоли. Я предположил, что нужно передать интегратору ID проекта, и гипотеза сработала!

Верификация в Google

Помимо «чёрных ящиков», на каждом этапе разработки большую роль играл Google.

Во-первых, как я уже писал выше, чтобы добраться до документации Push Provisioning API, нужно было подавать запрос в Google.

Во-вторых, у Google строгие гайдлайны по дизайну, которые до мелочей регламентируют визуал фичи. У сценария добавления карты в приложение чёткий алгоритм: например, когда пользователь оформляет карту, приложение обязательно должно показать ему, что он может подключить её в Google Pay.

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

В-четвёртых, в конце Google провёл валидацию — получил приложение и протестировал его. Только после этого он разрешил использовать фичу в релизных сборках.

Затем мы собрали альфа-сборку и передали приложение на проверку в банк. Для этого мы предоставили доступ к тестовым данным, чтобы сотрудники банка прошли конкретные шаги. Фичу релизили c выключенной feature toggle, чтобы она не была видна пользователям, пока банк не прошёл нужные сценарии и мы не исправили обнаруженные проблемы. А потом нужно было ещё раз получить финальный аппрув от Google. Наконец, мы дождались, когда какое-то количество пользователей обновит приложение и включили фичу.

Результаты

Для реализации фичи по добавлению банковской карты в Google Pay нужно было выполнить четыре функциональных требования. Мы сделали:

  • Первичную интеграцию с проверкой и получением всех нужных доступов. 

  • Полноценную интеграцию фичи с дизайном.

  • Интеграцию с банком, в которой было взаимодействие не только с SDK, но и с интегратором и токен сервис-провайдером.

  • Интеграцию в конкретный флоу, где после заказа карты в банке пользователь видит предложение добавить эту карту в Google Pay через приложение.

После реализации фичи мы продолжаем развивать приложение.

© Habrahabr.ru