Fear, hate and tokenization of bank cards in Google Pay

Tomcat

Professional
Messages
2,383
Reputation
4
Reaction score
408
Points
83
We have developed a feature for an Eastern European bank, thanks to which a customer can connect a card to Google Pay directly in the app. Five participants were involved in the project: we, the bank, Google Pay, an integrator, and a token service provider. The last three had white spots in the documentation, which took a month and a half to clear up, although the development on our part took only two weeks.

I'm Vladislav Kortikov, an Android developer at KODE. In the article, I told you what can wait for you when adding such a feature to a banking application. There are a lot of non-obvious points that are understood with pain, and perhaps one day this information will help someone save time and effort.

Why does a bank need tokenization?​

The bank wanted customers to be able to add cards to Google Pay directly from the mobile app. To do this, he needed to tokenize the cards first on the server side, and then in the application.

During the year, the bank transferred customers to VISA cards, and a server vendor with a VISA license tokenized them on the server. All that remained was to tokenize the cards in the app. In the summer of 2023, the bank transferred this task to our team, which had similar experience and worked with Google services.

Using the feature, the bank planned to increase the motivation of customers to install the app and DAU-daily active users. The logic was as follows:
  1. The bank needs as many customers as possible in the app, because it offers products and services there.
  2. It is convenient for the client to pay for purchases by phone and not search for a physical card in their pockets.
  3. To pay for purchases by phone, the customer can download the app and directly add a bank card to Google Pay.
  4. And in order for the client to add the card to Google Pay, the bank needs to make a feature.

Three black boxes​

To develop the feature, I needed to learn about a special API from Google Pay-the Push Provisioning API. There was no publicly available information about him. We contacted Google through the bank manager and requested documentation, which I studied together with the analyst.

After reading the documentation, we are left with three "black boxes"::
  1. Google Pay,
  2. Integrator,
  3. Service provider token.
The Android developer's documentation did not contain exact information about how the server works and how it communicates with the token service provider. The documentation described how everything should work on the client and gave hints for different cases. But it did not give an understanding of what the integrator did and whether he did it exactly in accordance with the documentation of the token service provider.

Therefore, when there were problems, I had to assume what exactly the integrator or the token service provider did wrong. I would re-open the documentation, search for small letters at the bottom, and hypothesize about changes. If it didn't work, I tested the following hypothesis. And when I hit a dead end, I went to communicate with Google Pay, an integrator, or a token service provider, and communication was delayed.

Main scenario for adding a card​

I divided adding the card into two stages: first I set up the framework, and then I made a full connection.

First stage: integration with the Google Pay SDK​

To interact with the Google Pay SDK, you had to configure integration with the Push Provisioning API. I did not write the logic and did not comply with the internal functional requirements of the feature, but only followed the requirements of the API documentation in order to make a minimal implementation for health checks. Then I asked Google for what was missing — for example, permission to test the Push Provisioning API for specific app versions. To do this, you had to fill out a questionnaire and describe the application configuration: pass the Android ID and hash of the signature key. This step helped reduce future errors.

I also wrote an add-on for the SDK to hide its external dependencies.

Second stage: full implementation of the scenario​

After I set up the interfaces, a framework for full-fledged connection appeared. We gathered the business requirements from Google's strict design requirements and the bank's requirements, and got to work.

Get OPC Request​

I have a middleware server-a layer between the bank's services and the mobile app. To add a card, pass the SDK a special OPC token. The app does not generate this token, and to get it, I send a Get OPC request to the middleware server. In the request, I pass three parameters about the user to the server:
  1. ID that the SDK provides-Stable Hardware ID.
  2. ID of the current Wallet that the user has installed.
  3. Card ID.
The server contacts the integrator, which communicates with the token service provider and returns the token back. However, I don't use the first two IDs to collect user information.

Logic for adding a card​

The user selects the card that they want to connect to Google Pay and starts the script for adding the card. I get three IDs-Stable Hardware ID, current Wallet ID, and User Card ID. I call the Get OPC method and get a token. Then I give the SDK token and call the Push Tokenize method in it.

Next, I pass control to the SDK, and it opens screens on its side like: "You want to add a card to Google Pay. Sign the necessary agreements." The user stays inside the banking app and goes through the scenario of adding a bank card: activates it, confirms consent to data processing. Upon successful completion of the script, the SDK returns the result to the app and the user sees that the card has been added to Google Pay. After that, the app may ask them to assign the card as the default payment method.

If the user has more than one active Google account on the device, then each of them needs its own Wallet ID. In this case, you need to select the currently active account that the user has set up, call the Get OPC request, send three IDs, and get a token.

Special scenarios​

In addition to the main scenario of adding a card, I needed to implement special scenarios.

Show the user in the app that they have added the card to Google Pay​

The SDK has two ways to show the user that the card has been added:
  1. The ListTokens method. Upon request, it returns a list of tokens that belong to the app — that is, a list of all our bank's cards. The app doesn't read data from other cards.
  2. The IsTokenized method. Upon request, the method returns true or false. I set the parameter as the last four digits of the card, and when the method returns true, I understand that there is a token for this card. However, if the four digits belong to a different bank card, the method will not report this. I will think that this is a card of our bank and I have its token. And I won't be able to check whether it's my own or someone else's, because Google monitors the safety of data from other banks.
I chose the first method because the second one might give out false information. But due to the fact that ListTokens is linked to the app, there was a problem: the method issued a token for the release app, but the token was needed for the test one.

How the problem was solved​

There are two types of Google Pay environments — test and release. To add cards in test mode, download the special file "android_pay_env_override_sandbox"from the link in the documentation and reload it. Then the Google Pay test environment will refer to the service provider's test environment.

I made a test and release build of the app. Now I had to tell Google that I knew how to work with the test environment and that I needed the test card for testing, even though it didn't exist in reality. I also had to inform the token service provider that I needed to work with the test card — so that the card passed the real scenario, but the provider did not consider it real.

As a result, when something did not go according to plan, we dealt with the settings of the token service provider, wrote to the integrator, and its employees worked on scripts and were not eager to solve the problem. A thorough study of the technical documentation helped. When the token service provider or integrator said that everything was fine on their side, everything was fine for us too, but nothing worked. So we showed them the Google documentation and said, "Look, this method should work this way, but it works differently for you." So we gradually communicated, clarified the "black boxes" and eventually received a token to test the application and show the user that he added the card to Google Pay.

Test case with incomplete manual card tokenization​

Also, to add a feature, you had to pass a number of mandatory test cases. One of them is Yellow Path: manually adding a card to Google Pay via the Google app. You need it in case the user started adding the card manually instead of using the app, and at the moment when they need to complete identification, they decided to cancel the addition. Google hasn't activated it yet, but it has already created a token, and the user hasn't confirmed it yet. Manual tokenization is required by Google Pay so that the user can finish adding the card manually.

In this case, the usual "run Push Tokenize" method doesn't help. It only works if you don't have the token yet and need to create a new one. Therefore, I checked the case using the ListTokens method, which returns the following data about this token:
  • ID token,
  • last four digits of the card,
  • which token service provider does it belong to: VISA or Mastercard,
  • card status — whether it is active.
When checking, a problem occurred: ListTokens did not return this data.

The entire data path looked like this: I passed the data, the service provider token contacted our backend, then the integrator, and the latter communicated with it and with Google Pay. At that time, the mobile app was communicating with the Google Rau SDK. And I didn't know 100% how the three participants in this interaction worked: Google Rau, a token service provider and integrator. I only knew exactly how the middleware server and the part with the SDK worked — because the SDK describes the cases that can occur as much as possible, although it does not explain how they are consistent with other integrations.

So, it was necessary to solve the problem and get the data using the ListTokens method. This took a long time, because all the data I had had to be passed to the integrator, which calculates the tokens and issues them to the application. It turned out that the service provider didn't fully fill out the token, and because of this, Google didn't get the app ID, couldn't figure out which tokens belong to our app, and gave them to us using the ListTokens method.

As a result, the team and I communicated with a token service provider, which did not always understand what we wanted to get from it. We also talked to the integrator, who expected an ID from us and said that we did not send it. We passed them the ID of the app that Google Pay gave us data for. The integrator said that the format is incorrect. We searched, researched, and even called the token service provider and integrator separately to understand what kind of ID they need, at what stage and who loses it, and how to transfer it to us.

We understood that the ID is not lost on our side — after all, we have it. Google's requirements reported that if you don't get the list, you probably have an incorrect parameter specified. We duplicated the parameter that the integrator was supposed to receive for our managers. We started discussing, testing, and verifying where the parameter came from, and why someone didn't pass it.

I had to make a lot of assumptions and conduct research. For example, I made all possible versions of the app and matched them to Google with signed keys to find a version that can get data from ListTokens. This was necessary to make sure that none of the" black boxes " got the app ID mixed up, since the test app differs from the release one by this ID and the signature key. To do this, you had to constantly change the project configuration. Each build took half an hour — a very long time. I checked all the versions and none of them were getting data, so I couldn't get a list of tokens from Google Pay. As a result, I discovered that the integrator has its own developer console, which has an ID for each created project. And the integrator did not ask for the device ID, but the ID of the project that is being created in their console. I assumed that you need to pass the project ID to the integrator, and the hypothesis worked!

Google Verification​

In addition to the "black boxes", Google played a big role at every stage of development.

First, as I wrote above, to get to the Push Provisioning API documentation, you had to submit a request to Google.

Secondly, Google has strict design guidelines that regulate the visual features to the smallest detail. The scenario of adding a card to the app has a clear algorithm: for example, when a user issues a card, the app must show them that they can connect it to Google Pay.

Third, in addition to separate feature testing, Google had mandatory test cases that the app had to pass.

Fourth, in the end, Google performed a validation — it received the app and tested it. Only then did he allow the feature to be used in release builds.

Then we built an alpha build and submitted the app to the bank for review. To do this, we have provided access to test data so that bank employees can complete specific steps. The feature was released with feature toggle disabled, so that it was not visible to users until the bank passed the necessary scenarios and we fixed the detected problems. And then I had to get the final appruv from Google again. Finally, we waited for a certain number of users to update the app and enabled the feature.

Results​

To implement the feature for adding a bank card to Google Pay, you had to meet four functional requirements. We did:
  • Initial integration with checking and getting all the necessary accesses.
  • Full integration of the feature with the design.
  • Integration with the bank, which included interaction not only with the SDK, but also with the integrator and token service provider.
  • Integration into a specific flow, where after ordering a card from a bank, the user sees an offer to add this card to Google Pay via the app.
After implementing the feature, we continue to develop the app.
 
Top