Direct Google Pay™ integration for Android
This guide will help you integrate with Revolut APIs directly to accept Google Pay payments in your Android application. This guide assumes that you have already integrated the Google Pay API on your Android app and are ready to process payments through Revolut as your payment processor.
Before integrating, review and accept the Google Pay Acceptable Use Policy and Terms of Service.
How it works
From an implementation perspective, integrating Google Pay with Revolut involves the following components:
- Server-side: You need to set up endpoints on your server that create orders and process payments using the Revolut Merchant API.
- Client-side: Your Android app uses the Google Pay API to collect encrypted payment tokens and communicates with your server.
- Endpoint for webhooks: Optionally, you can set up an endpoint to receive webhook events from the Merchant API to track the payment lifecycle. For more information, see: Use webhooks to keep track of the payment lifecycle.
The order and payment flow is as follows:
- Checkout and order creation: The customer navigates to checkout. Your app creates an order via the Revolut Merchant API, obtains the order token, and initialises the Google Pay button with Revolut-specific gateway parameters.
- Google Pay authorisation: The customer taps the Google Pay button and authorises the payment. Google Pay returns an encrypted payment token to your app.
- Payment processing: Your app sends the encrypted Google Pay token to create a payment request via the Revolut Merchant API. Your app then polls the payment status until a final state or challenge is returned.
- 3D Secure challenge (if required): If 3DS authentication is required, your app displays the challenge to the customer, who completes the authentication.
- Payment result: Your app shows the payment result to the customer.
- Webhooks: Your server receives webhook notifications about payment events.
For more information about the order and payment lifecycle, see: Order and payment lifecycle.
Implementation overview
Check the following high-level overview on how to implement direct Google Pay integration in your Android app:
- Configure redirect URL
- Create an order
- Initialise Google Pay with Revolut gateway parameters
- Create a payment request
- Handle payment results and track status
Before you begin
Before you start this tutorial, ensure you have completed the following steps:
- Get started: 1. Apply for a Merchant account
- Get started: 2. Generate the API keys
- Integrated the Google Pay API in your Android app following the Google Pay Android developer documentation and tutorial
Implement Direct Google Pay integration
1. Configure redirect URL
Revolut only accepts HTTPS values in the 3ds_redirect_uri parameter. After a 3D Secure (3DS) challenge, the issuer's ACS redirects the customer to this URL. To return the customer to your Android app instead of leaving them in the browser, you must register the HTTPS URL as an Android App Link and verify your domain.
-
Choose a HTTPS URL you control.
Example:https://pay.example.com/3ds-return. You will pass this exact URL as3ds_redirect_uriwhen creating the order in the next step. -
Add an App Link intent filter in your manifest for the activity that should receive the 3DS return:
AndroidManifest.xml<activity
android:name=".ThreeDsReturnActivity"
android:exported="true">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="pay.example.com"
android:pathPrefix="/3ds-return" />
</intent-filter>
</activity> -
Host the Digital Asset Links file to verify that the HTTPS domain belongs to your app:
https://pay.example.com/.well-known/assetlinks.json[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": [
"12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF"
]
}
}
]
Use the SHA-256 fingerprint from the certificate you use to sign your release build. You can obtain it from your signing configuration (for example, via ./gradlew signingReport or the Play Console if you use Play App Signing).
If you do not want to use an HTTPS redirect back into your app, omit 3ds_redirect_uri and handle the 3DS challenge in a WebView as described in Step 5.
2. Create an order
Create an order from your backend forwarding the data collected during checkout, and including your Secret API key in the authorisation header. We recommend creating the order when the customer is at your checkout, and you are ready to take the payment.
Send a POST request to the Create an order endpoint with the order details:
{
"amount": 100,
"currency": "GBP",
"3ds_redirect_uri": "https://pay.example.com/3ds-return",
"customer": {
"email": "customer@example.com"
}
}
| Parameter | Description | Required |
|---|---|---|
amount | The order amount in the smallest currency unit (e.g., cents). For £1.00, use 100. | Yes |
currency | The three-letter ISO 4217 currency code (e.g., GBP, USD, EUR). | Yes |
capture_mode | Determines when funds are captured. Use automatic for immediate capture or manual for delayed capture. | No |
3ds_redirect_uri | The HTTPS URL where the customer will be redirected after completing the 3D Secure challenge (e.g., https://pay.example.com/3ds-return). Use the App Link you configured in Step 1. If not provided, you must handle the 3DS challenge in a WebView within your app. | No |
customer | Customer details for saving payment methods. Required if you want to save the payment method for future use. Include email for new customers or id for existing customers. | No |
For complete details on all available parameters, see: Merchant API: Create an order.
Once the order is created successfully, the Merchant API returns a JSON object. Extract the token from the response as you will need it in the next step:
{
"id": "64d38f09-d04e-a9b5-b394-991f8a527eb3",
"token": "1112e405-c0b1-4a3f-a50d-e8acd66c7a8e",
"type": "payment",
"state": "pending",
"created_at": "2023-08-09T13:05:13.697830Z",
"updated_at": "2023-08-09T13:05:13.697830Z",
"capture_mode": "automatic",
"amount": 100,
"currency": "GBP",
"outstanding_amount": 100,
"checkout_url": "https://checkout.revolut.com/payment-link/999515ab-be9e-4477-b879-85b85c1576c1",
}
3. Initialise Google Pay with Revolut gateway parameters
Now you need to initialise the Google Pay button in your Android app so that your customer can initiate the payment. To initialise the button, follow the official Google guide to implement Google Pay on Android.
When integrating Google Pay, follow the Google Pay Android brand guidelines. Use the official button and logo assets as provided, without altering their colours, size, or appearance.
Payment configuration parameters
When requesting a payment token from Google for your payment provider, you need to set two specific values so that the payment token is encrypted specifically for Revolut as your payment processor.
Here's how to configure these parameters in your Android app, following the Google Pay tutorial:
Configure the tokenisation specification
First, configure how the payment token should be encrypted for Revolut:
import org.json.JSONArray
import org.json.JSONObject
// Order `token` obtained by creating an order in the Merchant API
val orderToken: String = createOrder().token
// Configure the tokenisation specification for Revolut
private fun getTokenizationSpecification(orderToken: String): JSONObject {
return JSONObject().apply {
put("type", "PAYMENT_GATEWAY")
put("parameters", JSONObject(mapOf(
"gateway" to "revolut",
"gatewayMerchantId" to orderToken // Order token from Step 2
)))
}
}
| Parameter | Description |
|---|---|
createOrder() | Function in your app that calls your backend to create an order (Step 2) and returns the token from the response. |
gateway | Set to "revolut" to identify Revolut as your payment processor. |
gatewayMerchantId | Set to the orderToken variable, which contains the order token you received from Step 2: Create an order. This value must be dynamically obtained for each payment to maintain security. |
Define card parameters
Next, specify which card networks and authentication methods you want to support:
// ... getTokenizationSpecification() from previous step
// Configure card parameters with allowed networks and authentication methods
private fun getCardParameters(): JSONObject {
return JSONObject(mapOf(
"allowedCardNetworks" to JSONArray(listOf(
"VISA",
"MASTERCARD",
"AMEX"
)),
"allowedAuthMethods" to JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS"))
))
}
| Parameter | Description | Supported options |
|---|---|---|
allowedCardNetworks | Card networks that you want to accept for Google Pay payments. Choose the networks you want to support in your implementation. | VISA, MASTERCARD, AMEX |
allowedAuthMethods | Authentication methods for card credentials. | PAN_ONLY (physical card details), CRYPTOGRAM_3DS (tokenised virtual card) (subject to confirmation with Revolut) |
For complete details on the allowedCardNetworks parameter and card network configuration, refer to Google Pay Android API CardParameters
Supported card networks:
Revolut supports the following card networks for Google Pay processing:
- VISA
- MASTERCARD
- AMEX (American Express)
Card network support may vary depending on your merchant settlement country and agreement with Revolut. Consult with your Integration Specialist or Account Manager to confirm which card networks are enabled for your merchant account.
Supported authentication methods:
Google Pay provides two different authentication methods for card credentials. Revolut's integration supports both types, and merchants should enable the methods they need in their Google Pay configuration by setting the allowedAuthMethods parameter.
| Description | 3DS | Processing | |
|---|---|---|---|
PAN_ONLY | Physical card details stored in Google Pay | Required, standard 3DS flow applies | When you submit a payment with PAN_ONLY credentials, Revolut automatically handles the 3DS authentication. If the issuing bank requires authentication, you will receive a 3DS challenge response as described in Step 5: Handle the payment response. |
CRYPTOGRAM_3DS | Tokenised virtual card stored on device | Not required, authentication is performed by Google Pay | These tokens are pre-authenticated by Google Pay and typically do not require additional 3DS authentication, as Google Pay has already performed strong customer authentication. |
When configuring your Google Pay implementation, specify both authentication methods in the allowedAuthMethods array as shown in the code example above. Revolut automatically processes the appropriate authentication flow based on the token type received from Google Pay.
Configure billing address collection
To process Google Pay payments, configure Google Pay to collect the customer's billing address (billingAddressRequired: true) in "FULL" format. The example below shows the recommended settings.
// Configure card parameters with billing address collection
private fun getCardParameters(): JSONObject {
return JSONObject(mapOf(
"allowedCardNetworks" to JSONArray(listOf(
"VISA",
"MASTERCARD",
"AMEX"
)),
"allowedAuthMethods" to JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")),
// Add billing address parameters
"billingAddressRequired" to true,
"billingAddressParameters" to JSONObject(mapOf(
"phoneNumberRequired" to false,
"format" to "FULL"
))
))
}
| Parameter | Description |
|---|---|
billingAddressRequired | Whether to collect billing address from the customer. Set to true. |
billingAddressParameters.phoneNumberRequired | Whether phone number is required as part of the billing address. Set to false. |
billingAddressParameters.format | Level of detail to collect for the billing address. Set to "FULL". |
Combine card parameters with tokenisation specification
Now extend your card configuration with the tokenisation specification:
// ... getTokenizationSpecification() and getCardParameters() from previous steps
// Combine parameters with tokenisation specification
private fun getCardPaymentMethod(orderToken: String): JSONObject {
return JSONObject().apply {
put("type", "CARD")
put("parameters", getCardParameters())
put("tokenizationSpecification", getTokenizationSpecification(orderToken))
}
}
Build the complete allowed payment methods configuration
Finally, create the complete payment methods array:
// ... previous functions from steps
// Build the complete allowed payment methods configuration
private fun getAllowedPaymentMethods(orderToken: String): JSONArray {
return JSONArray().put(getCardPaymentMethod(orderToken))
}
Next steps
Follow the rest of the Google guide to initiate the payment process with Google Pay.
The result of all these steps should be an encrypted payload (Payment token) that is ready to be used for payment processing.
4. Create payment request
In this step, your backend submits the Google Pay token and required metadata to Revolut to initiate the payment.
Initiate payment and receive PaymentData
Use the Google Pay PaymentsClient to call loadPaymentData() with the PaymentDataRequest you build from your Google Pay configuration. This is the point where the Google Pay UI is shown and the user authorises the payment. In the official Google guide, this is covered in Initiate a payment.
Use the Activity Result API with GetPaymentDataResult to receive the PaymentData response and handle success, cancellation, or errors. Once you have the PaymentData, continue with the extraction step below to retrieve the encrypted token and send it to your backend.
// Build the request from your Google Pay configuration
val paymentDataRequest = createPaymentDataRequest()
// Start the Google Pay flow and attach the result to the launcher
val paymentDataTask = paymentsClient.loadPaymentData(paymentDataRequest)
paymentDataTask.addOnCompleteListener(paymentDataLauncher::launch)
Extract payment details from PaymentData
From the PaymentData response, extract:
- Payment token from
paymentMethodData.tokenizationData.token - Billing address from
paymentMethodData.info.billingAddress - Cardholder name from the billing address
name
Here's how to extract this data in your Android app:
private val paymentDataLauncher = registerForActivityResult(GetPaymentDataResult()) { taskResult ->
when (taskResult.status.statusCode) {
CommonStatusCodes.SUCCESS -> {
taskResult.result?.let { paymentData ->
val paymentInfo = paymentData.toJson()
val paymentMethodData = JSONObject(paymentInfo)
.getJSONObject("paymentMethodData")
// Extract the encrypted token
val tokenizationData = paymentMethodData
.getJSONObject("tokenizationData")
val encryptedToken = tokenizationData.getString("token")
// Extract billing address
val billingAddress = paymentMethodData
.getJSONObject("info")
.optJSONObject("billingAddress")
// Extract the name for `cardholder_name` (if provided by Google Pay)
val cardholderName = billingAddress?.optString("name")
// Send the token + billing details to your backend. Your backend should map Google Pay
// `billingAddress` to the Revolut `billing_address` structure and pass `cardholder_name`.
sendPaymentToBackend(encryptedToken, billingAddress, cardholderName)
}
}
CommonStatusCodes.CANCELED -> Unit // Handle the user cancelling the payment
CommonStatusCodes.DEVELOPER_ERROR -> Unit // Handle errors the API returns (it.status: Status)
else -> Unit // Handle internal and other unexpected errors
}
}
For complete details on the PaymentData response structure, see Google's PaymentData documentation.
Send these values to your backend server.
Send payment details to Revolut
Besides the encrypted token, Revolut expects billing_address and cardholder_name in the payment request, so your backend must extract them from the Google Pay PaymentData response:
- Map the Google Pay
billingAddressto the Revolutbilling_addressstructure - Extract the
billingAddress.nameand send it ascardholder_name
Send a POST request to the Pay for an order endpoint:
{
"payment_method": {
"type": "google_pay",
"token": "{GOOGLE_PAY_TOKEN}", // Extracted from Google Pay `PaymentData`
"cardholder_name": "John Doe", // Extracted from Google Pay `PaymentData` billing address `name`
"billing_address": {
"street_line_1": "Example Street 123",
"street_line_2": "II/123",
"region": "London",
"city": "London",
"country_code": "GB",
"postcode": "123456"
},
"environment": {
"type": "browser",
"time_zone_utc_offset": 180,
"color_depth": 48,
"screen_width": 1920,
"screen_height": 1080,
"java_enabled": false,
"challenge_window_width": 640,
"browser_url": "https://www.mystore.com/webview/",
"client_ip": "74.23.11.101",
"user_agent": "Chrome 13.3",
"accept_header": "application/text",
"locale": "en-PH"
}
}
}
Payment request object:
| Parameter | Description | Format | Required |
|---|---|---|---|
payment_method | Payment method details for Google Pay. | Object | Yes |
Payment method object:
| Parameter | Description | Format | Required |
|---|---|---|---|
type | The type of the payment method. Must be google_pay. | String | Yes |
token | UTF-8 encoded, serialised JSON object containing the encrypted payment token returned by Google Pay. See Google's documentation for the expected token structure. | String (JSON) | Yes |
cardholder_name | Cardholder name. Extract the name value from the Google Pay PaymentData billing address and pass it here. | String | Yes |
billing_address | Billing address provided by the customer in Google Pay. Preprocess and map it to the Revolut Address format before sending. | Object | Yes |
shipping_address | Shipping address provided by the customer in Google Pay. Preprocess and map it to the Revolut Address format before sending. | Object | No |
environment | Object containing information about the environment where the customer makes the payment. This information is required for the 3DS challenge, since some banks might execute different challenges based on the object's parameters. | Object | Yes |
Address object:
| Parameter | Description | Required |
|---|---|---|
street_line_1 | Street line 1 information. | No |
street_line_2 | Street line 2 information. | No |
region | The region associated with the address. | No |
city | The city associated with the address. | No |
country_code | The country associated with the address (ISO 3166-1 alpha-2). | Yes |
postcode | The postcode associated with the address. | Yes |
Environment object:
Locale should be provided in <language> or <language>-<country> format, where language is an ISO 639-1 code and country is an ISO 3166-1 alpha-2 code.
| Parameter | Description | Format | Required |
|---|---|---|---|
type | Type of the environment where payment takes place. Currently, only browser is supported. | String | Yes |
time_zone_utc_offset | The offset between the customer's current time zone and UTC in minutes. | Integer | Yes |
color_depth | Supported color depth of the customer's device. If in doubt, use the default value of 48. | Integer | Yes |
screen_width | Width of the screen where checkout is presented in pixels. | Integer | Yes |
screen_height | Height of the screen where checkout is presented in pixels. | Integer | Yes |
java_enabled | Indicates whether Java is enabled on the checkout screen. If in doubt, set to false. | Boolean | Yes |
challenge_window_width | The desired width of the challenge window in pixels. A recommended ratio is 1/3 of the screen_width. | Integer | No |
browser_url | The URL of the checkout page where the challenge will be displayed. | String | Yes |
client_ip | The IP address of the customer's device. | String | No |
user_agent | The user agent string of the customer's browser or app. | String | No |
accept_header | The Accept header from the customer's HTTP request. | String | No |
locale | The locale of the customer's device in <language> or <language>-<country> format. | String | No |
5. Handle payment results and track status
After sending the payment request in Step 4, you need to track the payment status and handle different scenarios.
The recommended approach for tracking payment status is to use webhooks. Webhooks provide reliable, server-to-server notifications about order and payment lifecycle events and ensure you don't miss any status updates.
For detailed information on setting up webhooks, see: Use webhooks to track order and payment lifecycle.
While the immediate payment response is useful for updating your app's UI, webhooks are your source of truth for the actual payment status. Your backend should listen for webhook events to determine the next action for each payment.
In the majority of cases, the response for a Google Pay payment initiated on a mobile device will be a successful transaction. This is because Google Pay usually removes the need for authentication.
However, there are scenarios where the payment might require a 3DS challenge:
- Google Pay and/or the issuer have not been able to generate a fully authenticated payment token.
- The transaction was soft declined by the issuer and re-authentication is required to confirm the transaction. Revolut has a mechanism to automatically retry the transaction after a soft decline and enforce 3DS authentication.
You should handle the following scenarios:
This is the most common scenario for Google Pay payments. The payment completes successfully without requiring additional authentication.
Webhooks expected:
| Webhook event | Description |
|---|---|
ORDER_AUTHORISED | The payment has been authorised |
ORDER_COMPLETED | The payment has been captured and completed |
What to do:
- When you receive the
ORDER_COMPLETEDwebhook, fulfil the order (ship goods, grant access, etc.). - Depending on how you capture orders:
- For automatic capture mode, wait for
ORDER_COMPLETEDbefore fulfiling the order. - For manual capture mode,
ORDER_AUTHORISEDcan be considered a final successful state. You can now capture the order manually when ready. TheORDER_COMPLETEDwebhook will be sent after you capture.
- For automatic capture mode, wait for
Most Google Pay payments on mobile will follow the frictionless flow, as Google Pay typically removes the need for authentication.
Implementation checklist
Before deploying your implementation to your production environment, complete the checklist below to ensure everything works as expected:
For a comprehensive list of integration requirements, refer to the Google Pay Android integration checklist.
- Configured and verified an HTTPS App Link for
3ds_redirect_uri. - Successfully created an order using the Merchant API and received the order
token. - Configured Google Pay button with the correct gateway parameters:
gateway: "revolut"andgatewayMerchantId: <order_token>. - Verified that the
gatewayMerchantIdis dynamically set to the order token for each new payment. - Successfully processed a test payment and handled the payment response.
- Set up webhook URLs on your server to receive order and payment updates.
- Implemented webhook handling logic to track payment status based on events (
ORDER_COMPLETED,ORDER_AUTHORISED,ORDER_PAYMENT_AUTHENTICATION_CHALLENGED, etc.). - Verified that webhooks for successful payments (
ORDER_COMPLETED) trigger order fulfillment. - Verified that webhooks for authentication challenges (
ORDER_PAYMENT_AUTHENTICATION_CHALLENGED) trigger retrieval and handling of theauthentication_challengeobject. - Handled
three_dschallenges by redirecting toacs_urlor loading in a WebView. - Handled
three_ds_fingerprintchallenges by decodingfingerprint_htmlfrom base64 and loading the resulting HTML in a WebView. - Tested both successful and declined payment scenarios in Sandbox using the Google Pay test amounts.
- Implemented proper error handling for failed payments and displayed appropriate messages to users.
Congratulations! You've successfully integrated direct Google Pay with the Revolut Merchant API in your Android app.