Hi, Habr!
My name is Igor, I'm an Android developer on the Trinity Digital team. Today I want to talk about the cool tool -
Google Pay API .

')
So , if you can make purchases in your application, and at the same time you are not using In-app Billing (processing is not responsible for Google Play), then you most likely have “Payment by card” among the payment options. This means that every time you have to send a user to enter card data or on beautifully designed screens with a card, or on the website of your payment service provider (hereinafter - payment processor). Have you already counted how many actions the user will have to make in order to pay for the coveted order? Yeah, and now imagine that he can perform the same target action in just two tapas. We also introduced and thought, why not give users the opportunity? The main conditions for success - the seller must be
registered with Google and the payment processor must cooperate with Google.
The list of Russian banks that cooperate with Android Pay:
AK Bars Bank
Alfa Bank
BINBANK
Promsvyazbank
VTB 24
Bank opening
MTS Bank
Raiffeisen Bank
Roketbank
Agricultural Bank
Russian Standard Bank
Sberbank
Tinkoff Bank
Point
Yandex money
How everything will look for the
user : he gets on the screen for choosing the type of payment in your application, clicks on the button “Pay via Google”, selects the desired card or leaves the one specified by default, presses the confirmation button. Done!
Remember that Google Pay API allows users to select any card linked either to a Google account or added to Google Pay.
We now turn directly to the
integration .
Consider the steps:
- Layout
- Code
- Testing
- Send for manual check
- Release
1. Layout
The first thing worth mentioning is to warn designers about
guidelines . Briefly by points:
- On screens BEFORE OR on the screen where the button “Pay via Google” will be located, the purchase price must be indicated;
- allow users to change order data, select payment type [, change address];
- never show payment details in full (any numbers, dates, and so on);
- once again - “Pay via Google” - this is the inscription that should be on your button if you make an application that supports the Russian language;
- Google recommends using standard buttons. If you want to use a dark theme or even a button with your own design, then you should write in those. support at androidpay-api-support@google.com . But even on a custom button, there should be a Google logo and an inscription ... yes, I hope you understood :);
- there is no limit to the width, the minimum button height is 40dp. If you do higher / wider, remember that the text should be centered.
Compliance with these items will allow you to quickly pass all checks and get into the white list.
2. Code
For Google payment to work, Google Play Services must be version 11.4 or later on the user's phone. But do not worry, there is a special method that will tell you whether you can make a payment or whether it is worth hiding the button.
To begin with, add the necessary dependencies to the build.gradle of the application level. Before implementation, check the relevance of the versions!
dependencies { compile 'com.google.android.gms:play-services-wallet:11.4.0' compile 'com.android.support:support-v4:24.1.1' compile 'com.android.support:appcompat-v7:24.1.1' }
Next, update AndroidManifest:
<application> ... <meta-data android:name="com.google.android.gms.wallet.api.enabled" android:value="true" /> </application>
Now there is quite a bit:
- Create a PaymentsClient in your Activity or Fragment. In order not to clutter up these classes, you can put all the code in GooglePaymentUtils methods, for example. Then:
class MainActivity : AppCompatActivity() { private lateinit var googlePaymentsClient: PaymentsClient ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... googlePaymentsClient = GooglePaymentUtils.createGoogleApiClientForPay(context) } ... }
object GooglePaymentUtils { fun createGoogleApiClientForPay(context: Context): PaymentsClient = Wallet.getPaymentsClient(context, Wallet.WalletOptions.Builder() .setEnvironment(WalletConstants.ENVIRONMENT_TEST) .setTheme(WalletConstants.THEME_LIGHT) .build()) }
Pay attention to the constants:
WalletConstants.ENVIRONMENT_TEST - as long as Google does not allow access to the combat environment, you must use it to test the flow of payment yourself. Do not be alarmed when you see a warning in the Google Pay dialog that the application is not recognized.
WalletConstants.THEME_LIGHT is a light topic of dialogue, there is also a dark one.
- Great, we have a client, now we are ready to make a request whether it is possible to use the payment at all and show the button.
object GooglePaymentUtils { fun checkIsReadyGooglePay(paymentsClient: PaymentsClient, callback: (res: Boolean) -> Unit) { val isReadyRequest = IsReadyToPayRequest.newBuilder() .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD) .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD) .build() val task = paymentsClient.isReadyToPay(isReadyRequest) task.addOnCompleteListener { try { if (it.getResult(ApiException::class.java)) // , callback.invoke(true) else
PAYMENT_METHOD_CARD , PAYMENT_METHOD_TOKENIZED_CARD - they say that we want to see cards from a Google user account and cards linked to Android Pay.
- If we can show a button, then we must hang a click handler on it.
btnPaymentByGoogle.setOnClickListener { val request = GooglePaymentUtils.createPaymentDataRequest(price) AutoResolveHelper.resolveTask<PaymentData>(googlePaymentsClient.loadPaymentData(request), context, REQUEST_CODE) }
Here, remember that price is a line. And most importantly, even if you call AutoResolveHelper.resolveTask from a fragment, the result will still come in activations (more on that later) [at the time of writing this article works this way, AutoResolveHelper does not know how to return the result to the fragment].
fun createPaymentDataRequest(price: String): PaymentDataRequest { val transaction = createTransaction(price) val request = generatePaymentRequest(transaction) return request } fun createTransaction(price: String): TransactionInfo = TransactionInfo.newBuilder() .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL) .setTotalPrice(price) .setCurrencyCode(CURRENCY_CODE) .build() private fun generatePaymentRequest(transactionInfo: TransactionInfo): PaymentDataRequest { val tokenParams = PaymentMethodTokenizationParameters .newBuilder() .setPaymentMethodTokenizationType (WalletConstants.PAYMENT_METHOD_TOKENIZATION_TYPE_DIRECT) .addParameter("publicKey", TOKENIZATION_PUBLIC_KEY) build() return PaymentDataRequest.newBuilder() .setPhoneNumberRequired(false) .setEmailRequired(true) .setShippingAddressRequired(true) .setTransactionInfo(transactionInfo) .addAllowedPaymentMethods(SUPPORTED_METHODS) .setCardRequirements(CardRequirements.newBuilder() .addAllowedCardNetworks(SUPPORTED_NETWORKS) .setAllowPrepaidCards(true) .setBillingAddressRequired(true) .setBillingAddressFormat(WalletConstants.BILLING_ADDRESS_FORMAT_FULL) .build()) .setPaymentMethodTokenizationParameters(tokenParams) .setUiRequired(true) .build() }
Here CURRENCY_CODE = “RUB”.
WalletConstants.TOTAL_PRICE_STATUS_FINAL - we say that the purchase price is final and will not change anymore.
There are also options:
WalletConstants.TOTAL_PRICE_STATUS_ESTIMATED - approximate cost, and may change, for example, after specifying the address.
WalletConstants.TOTAL_PRICE_STATUS_NOT_CURRENTLY_KNOWN - do not yet know what the cost.
I can not say how the last two constants behave in practice, since I did not check ¯ \ _ (ツ) _ / ¯.
Let us dwell on PaymentMethodTokenizationParameters and its setPaymentMethodTokenizationType method:
- PAYMENT_METHOD_TOKENIZATION_TYPE_PAYMENT_GATEWAY
is used only if your payment processor is listed:
Adyen
Braintree
Paysafe
Stripe
Vantiv
WorldPay
Then instead of .addParameter("publicKey", TOKENIZATION_PUBLIC_KEY)
you must write .addParameter("gateway", "yourGateway")
.addParameter("gatewayMerchantId", "yourMerchantIdGivenFromYourGateway")
- Otherwise, the above type PAYMENT_METHOD_TOKENIZATION_TYPE_DIRECT is used .
To do this, you need to request the public key from the payment service provider and transfer it to the .addParameter("publicKey", TOKENIZATION_PUBLIC_KEY)
It now remains to create a request.
.setPhoneNumberRequired - whether the user should enter a number.
.setEmailRequired - whether the user should enter an email
.setShippingAddressRequired - whether the user should select a country. Here you can limit the number of countries for which this transaction will be executed.
.addAllowedPaymentMethods - we have WalletConstants.PAYMENT_METHOD_CARD - cards from google account, WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD - cards added to Google Pay.
In CardRequirements, we indicate that Visa, Mastercard and other cards must work (MIR, for example)
val SUPPORTED_NETWORKS = arrayListOf(WalletConstants.CARD_NETWORK_OTHER, WalletConstants.CARD_NETWORK_VISA, WalletConstants.CARD_NETWORK_MASTERCARD)
- Everything, we created a request, sent it through the client and wait for the result through AutoResolveHelper.
As you remember, the result will come in the activity .
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { LOAD_PAYMENT_DATA_REQUEST_CODE -> { when (resultCode) { Activity.RESULT_OK -> { if (data == null) return val paymentData = PaymentData.getFromIntent(data) } Activity.RESULT_CANCELED -> {
That's all, in paymentData you will have a token that should be given to your server. Further logic depends on your payment processor.
3. Testing
Nothing complicated, just check that the
WalletConstants.ENVIRONMENT_TEST constant is
set , and go through the entire flow. There will be no debit from the card, you will receive a test token, so the payment processor must reject the payment.
4. Send to manual check.
Congratulations! You are ready to send your debug build for manual verification to Google.
Some tips:
- If your application only supports Russian, then prepare screenshots with directions on where to click.
- If there is any specificity in the order process, then describe in detail.
- Create a test account specifically for Google and send directly to the build.
Send the build to
androidpay-api-support@google.com and wait for an answer.
5. Release
You were told that everything is good and you can release the application. First of all, you will be asked to
activate the application at (from the merchant account).
Next you may be asked to send a PCI Compliance. These documents confirm that your payment processor complies with card security standards. Ask him and send in support.
Once you have completed these two items, you will be told that you can change
WalletConstants.ENVIRONMENT_TEST to
WalletConstants.ENVIRONMENT_PRODUCTION . You may also need to change the TOKENIZATION_PUBLIC_KEY if you used the key from the test environment of your payment processor.
That's all, now test the real payment and you can release a release to the market!
Thanks and good luck!