A short article on Android Push Provisioning API – which is necessary for interaction between a banking application and Google Pay.
There is practically no information in the public domain on the Internet, since the thing is quite specific and information from Google is open only to employees of financial institutions – card issuers
https://support.google.com/google-pay-and-wallet-console/answer/15157842?hl=en
If you think that you should have access to this information, you can request it
https://developers.google.com/pay/issuers/request-access
Implementation details cannot be mentioned, so I will write in general terms
First, you need to add the tapAndPay dependency to gradle, also download the tapAndPay SDK from the link above and place it in the folder
C:\Users\\AppData\Local\Android\Sdk\extras\google
To debug in the emulator, you need to enable sandbox mode, for this we add the file android_pay_env_override_sandbox to the download folder on the device
$ adb shell touch /sdcard/Download/android_pay_env_override_sandbox $ adb reboot
before adb don’t forget to put the path to it if the folder is not selected
for MacOS ~/Library/Android/sdk/platform-tools/adb for Windows C:\Users\\AppData\Local\Android\Sdk\platform-tools\adb
You can turn off sandbox mode by deleting the file
$ adb shell rm /sdcard/Download/android_pay_env_override_sandbox $ adb reboot
You need to add the app to the whitelist, as Google only allows affiliated financial institutions to access the Push Provisioning API. Therefore, in order for the card issuer app to call the API, the app must be granted access to the API. You can do this here
https://support.google.com/faqs/contact/pp_api_allowlist?hl=en
If the app is not whitelisted, the API will return the TAP_AND_PAY_UNAVAILABLE error. If you use applicationIdSuffix suffixes, you need to add a package with them for it to work.
Google Pay performs an authentication check when calling via the API. This check will fail if the device has root privileges.
You need to add a UI for it to work, there is a peculiarity here in that the button for adding a card and the Google Pay logo on the card image and other buttons related to Google Pay must exactly match the Google design, otherwise it will not pass moderation.
The Google Pay inscription should not be abbreviated, should be in English, the manual page provides the correct translations for the “Add to Google Pay” button
All Google Pay setup buttons must be at least 139 dp wide, there must always be at least 8 dp of free space above, below, left and right of the Google Pay button, do not place graphics or text in the free space.
How Google Pay works in general can be read here
https://developers.google.cn/android/guides/overview?hl=en
The card is added through a request in the tapAndPay client, in which you first need to fill in the user data – name, country, address, apartment, city, region, postal code and phone, if all this is not specified in the request, the user will have to enter it in the Google Pay application when adding.
After that, you need to specify the Opaque Payment Card in the request – these are encrypted objects created by the card issuer and transferred to Google Pay during initialization. Each Token Service Provider (TSP) has its own specifications for OPC formatting and encryption methods for card issuers.
An OPC can be generated by following these steps.
Exchange keys with the TSP. These keys are used to encrypt and decrypt the OPC.
Create and encrypt the OPC according to the TSP documentation.
Work with the TSP to ensure the OPC is valid and encrypted correctly.
The OPC that is passed to Google must be a Base64 encoded string. If the TSP rejects the OPC for any reason, Google Pay will show the user a generic error message.
Also specify the type of banking network – Visa, Mastercard or other, there are 7 in total, and also the token provider – Visa, Mastercard and others, there are 10 in total, all of which are constants in the client class, also the card name and the last 4 digits of the card number are required. When you click “add card”, the request will be sent to Google Pay and the user will be redirected to the Google Pay application if it is installed, and if not, you need to add the appropriate logic for installation from the Play Market.
For operations with the wallet, you need the TokenReferenceId and StableHardwareId parameters – from the names, in principle, it is clear what this is.
To make the statuses and errors when working with the API when outputting to the console clear, it is convenient to make a function with a decoding of error constants and statuses, I did it like this
private fun logResult(pair: Pair<Boolean, String>) { if (pair.first) { logd(pair.second, "GooglePay") } else { loge(pair.second, "GooglePay") } } private fun walletStatus(statusCode: Int) = when (statusCode) { TapAndPayStatusCodes.TAP_AND_PAY_NO_ACTIVE_WALLET -> false to "No active wallet" TapAndPayStatusCodes.TAP_AND_PAY_TOKEN_NOT_FOUND -> false to "card issuer token ID presented does not match the token in the valid wallet" TapAndPayStatusCodes.TAP_AND_PAY_INVALID_TOKEN_STATE -> false to "Specified token was found but could not be processed because it was not in a valid state" TapAndPayStatusCodes.TAP_AND_PAY_ATTESTATION_ERROR -> false to "Tokenization failed because the device did not pass the compatibility check" TapAndPayStatusCodes.TAP_AND_PAY_UNAVAILABLE -> false to "TapAndPay API cannot be called in the current app" else -> false to "wallet status code $statusCode" } private fun tokenStatus(statusCode: Int) = when (statusCode) { TapAndPay.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION -> false to "The token is in a valid wallet, but requires additional user authentication to use (step up authentication required)" TapAndPay.TOKEN_STATE_PENDING -> false to "Currently, tokens cannot be used for payment, but after a while they will be available" TapAndPay.TOKEN_STATE_SUSPENDED -> false to "The token has been temporarily suspended" TapAndPay.TOKEN_STATE_ACTIVE -> true to "The token is valid and can be used for payment" TapAndPay.TOKEN_STATE_FELICA_PENDING_PROVISIONING -> false to "The token is issued by TSP, but Felica has not been provisioned yet" TapAndPay.TOKEN_STATE_UNTOKENIZED -> false to "This state does not apply to push provisioning" else -> false to "token status code $statusCode" }
loge and logd in this example simply process messages and output if BuildConfig.DEBUG, and “GooglePay” is a tag to search in logcat
For convenience, you can add classes for token provider and network constants
enum class TokenProvider(val value: Int) { MASTERCARD(3), VISA(4), DISCOVER(5), EFTPOS(6), INTERAC(7), OBERTHUR(8), PAYPAL(9), JCB(13), ELO(14), GEMALTO(15) } enum class CardNetwork(val value: Int) { AMEX(1), DISCOVER(2), MASTERCARD(3), VISA(4), INTERAC(5), PRIVATE_LABEL(6), EFTPOS(7), MAESTRO(8), ID(9), QUICPAY(10), JCB(11), ELO(12) }
In API version 17.1.0, the listTokens method was added, which allows you to get some information about the added cards, namely
IssuerTokenId FpanLastFour DpanLastFour TokenServiceProvider Network IsDefaultToken PortfolioName
In a similar way, other actions with the card are carried out through the tapAndPay client – there are 15 methods in its interface in total, the most complex of which I described just above.