Accept Google Pay payments with automatic wallet handling, one-click checkout, and seamless Android integration.
Google Pay is automatically included in the drop-in when enabled in your session. The drop-in handles all Google Pay setup, button rendering, wallet interaction, and payment processing automatically.
- Google Pay appears automatically in the drop-in when it's enabled in your session configuration.
- The drop-in handles all Google Pay setup, so you don't need Google Pay-specific code.
- Google Pay uses the same
onSuccessandonErrorcallbacks as other payment methods for a unified integration. - Customers can pay using any card or payment method saved in their Google account.
- Native Android Google Pay UI for familiar and trusted checkout experience.
- One-click checkout for fast and frictionless payments.
- Enhanced security with tokenised payment credentials.
When a customer selects Google Pay:
- The customer taps "Google Pay" in the payment options list.
- The native Android Google Pay sheet opens.
- The customer selects their preferred payment method from Google Wallet.
- The customer confirms the payment in the Google Pay interface.
- The Google Pay sheet closes automatically.
- The payment is processed through Unity.
- Your
onSuccesscallback fires.
Configure Google Pay-specific settings for the payment button, merchant information, and transaction parameters.
The following properties are available for Google Pay configuration:
| Property | Description |
|---|---|
collectCvcCollectCvcMode? | Controls when to collect CVC during Google Pay transactions. Possible values:
|
billingAddressRequiredBoolean? | Requests a billing address from Google Pay. When enabled, use billingAddressParameters to specify phone number requirements. |
billingAddressParametersBillingAddressParameters? | Billing address options object. |
billingAddressParameters.phoneNumberRequiredBoolean? | Whether to require a phone number in the billing address. |
shippingAddressRequiredBoolean? | Requests a full shipping address from Google Pay. When enabled, use shippingAddressParameters to specify address restrictions. |
shippingAddressParametersShippingAddressParameters? | Shipping address options object. |
shippingAddressParameters.allowedCountryCodesList<String>? | Allowed country codes for shipping addresses (e.g., listOf("GB", "US", "FR")). |
shippingAddressParameters.phoneNumberRequiredBoolean? | Whether to require a phone number in the shipping address. |
blockedIssuerCountryCodesList<String>? | Blocks cards issued in the listed ISO country codes. Don't combine with methodConfig.global.allowedIssuerCountryCodes. |
Google Pay inherits the following settings from methodConfig.global:
| Property | Description |
|---|---|
acceptedCardNetworksList<CardNetworks>? | Which card brands to accept through Google Pay (e.g., listOf(CardNetworks.VISA, CardNetworks.MASTERCARD)). |
allowedIssuerCountryCodesList<String>? | Allowed card issuer countries as ISO country codes (e.g., listOf("GB", "US", "FR")). |
onCancel(paymentMethod: PaymentMethod, data: Any?) -> Unit | Called when user cancels Google Pay payment (closes sheet). |
This example shows a full Google Pay configuration with global callbacks and all Google Pay-specific options:
methodConfig = DropInMethodConfig(
// Global callbacks that apply to Google Pay
global = DropInGlobalConfig(
// Handle Google Pay cancellation
onCancel = { paymentMethod, data ->
if (paymentMethod == PaymentMethod.GOOGLE_PAY) {
Log.d("Checkout", "Google Pay payment cancelled: $data")
// Track analytics
analytics.trackEvent("google_pay_cancelled", mapOf(
"timestamp" to System.currentTimeMillis()
))
// Update UI
Toast.makeText(
context,
"Google Pay payment was cancelled. Please try again.",
Toast.LENGTH_SHORT
).show()
}
}
),
googlePay = DropInGooglePayConfig(
// CVC collection mode
collectCvc = CollectCvcMode.DEFAULT,
// Billing address collection
billingAddressRequired = true,
billingAddressParameters = BillingAddressParameters(
phoneNumberRequired = true
),
// Shipping address collection
shippingAddressRequired = true,
shippingAddressParameters = ShippingAddressParameters(
allowedCountryCodes = listOf("GB", "US", "FR", "DE"),
phoneNumberRequired = true
),
// Block specific issuer countries
blockedIssuerCountryCodes = null // Don't use with global.allowedIssuerCountryCodes
)
)Google Pay requires the following to function correctly:
- Android compatibility: Android 7.0 (API level 24) or higher.
- Google Play Services: Google Play Services must be installed and up to date.
- Google account: The customer must be signed in to a Google account with Google Wallet set up.
- HTTPS: Your backend endpoints must be served over HTTPS.
- Unity Portal configuration: Google Pay must be enabled and configured in the Unity Portal.
- Entry type: Google Pay only supports
entryType: Ecom.
Google Pay works through the standard implementation, with no Google Pay-specific code needed:
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.checkoutdropin.types.CheckoutDropInConfig
import com.pxp.checkout.components.checkoutdropincomponent.CheckoutDropInComponent
import com.pxp.checkout.models.DropInTransactionData
import com.pxp.checkout.models.DropInTransactionIntentData
import com.pxp.checkout.models.Environment
import com.pxp.checkout.models.EntryType
import com.pxp.checkout.models.IntentType
import com.pxp.checkout.checkoutdropin.types.DropInSubmitResult
import com.pxp.checkout.exceptions.BaseSdkException
@Composable
fun CheckoutScreen() {
val context = LocalContext.current
var sessionData by remember { mutableStateOf<SessionData?>(null) }
var checkoutDropInComponent by remember { mutableStateOf<CheckoutDropInComponent?>(null) }
var isLoading by remember { mutableStateOf(true) }
// Fetch session from backend (with Google Pay enabled)
LaunchedEffect(Unit) {
try {
val response = apiClient.post("/api/create-session") {
contentType(ContentType.Application.Json)
}
if (response.status.value == 200) {
val result = response.body<SessionResponse>()
if (result.success && result.data != null) {
sessionData = result.data
} else {
Log.e("Checkout", "Failed to create session: ${result.error}")
}
}
} catch (e: Exception) {
Log.e("Checkout", "Error creating session", e)
} finally {
isLoading = false
}
}
// Initialize Drop-in once session is loaded
LaunchedEffect(sessionData) {
sessionData?.let { session ->
val checkoutDropIn = CheckoutDropIn.initialize(
context = context,
config = CheckoutDropInConfig(
environment = Environment.TEST,
session = session,
ownerType = "MerchantGroup",
ownerId = "MERCHANT-1",
transactionData = DropInTransactionData(
currency = "GBP",
amount = 99.99,
entryType = EntryType.Ecom,
intent = DropInTransactionIntentData(
card = IntentType.Purchase // Google Pay uses card intent
),
merchant = "Demo Store",
merchantTransactionId = { UUID.randomUUID().toString() },
merchantTransactionDate = { Instant.now().toString() }
),
onGetShopper = {
Shopper(id = "shopper-123")
},
onSuccess = { result: DropInSubmitResult ->
Log.d("Checkout", "Google Pay payment successful!")
Log.d("Checkout", "System transaction ID: ${result.systemTransactionId}")
Log.d("Checkout", "Payment method: ${result.paymentMethod}") // Will be "GooglePay"
// CRITICAL: Verify on backend
coroutineScope.launch {
verifyPaymentOnBackend(result)
}
},
onError = { error: BaseSdkException ->
Log.e("Checkout", "Google Pay payment failed", error)
Toast.makeText(
context,
"Payment failed: ${error.message}",
Toast.LENGTH_LONG
).show()
}
)
)
checkoutDropInComponent = checkoutDropIn.create()
}
}
// Render Drop-in
if (isLoading) {
CircularProgressIndicator()
} else {
checkoutDropInComponent?.Content(modifier = Modifier.fillMaxWidth())
}
}Enable Google Pay in your session request:
// BACKEND: Create a session with Google Pay enabled
const sessionRequest = {
merchant: "MERCHANT-1",
site: "SITE-1",
sessionTimeout: 120,
merchantTransactionId: crypto.randomUUID(),
transactionMethod: {
intent: {
card: "Purchase" // Google Pay uses card intent
}
},
amounts: {
currencyCode: "GBP",
transactionValue: 99.99
},
allowedFundingTypes: {
wallets: {
googlePay: {
// Google Pay configuration from the Unity Portal will be used
// You can optionally specify merchant information here
merchantName: "Demo Store",
merchantId: "BCR2DN4TZXY2MLBU"
}
}
},
allowTransaction: true,
serviceType: "CheckoutDropIn"
};Drop-in supports two Google Pay payment flows, configured via the intent parameter:
Immediate, single-step payment where funds are captured right away.
import com.pxp.checkout.models.DropInTransactionData
import com.pxp.checkout.models.DropInTransactionIntentData
import com.pxp.checkout.models.EntryType
import com.pxp.checkout.models.IntentType
transactionData = DropInTransactionData(
currency = "GBP",
amount = 99.99,
entryType = EntryType.Ecom,
intent = DropInTransactionIntentData(
card = IntentType.Purchase // Pay Now flow - Google Pay uses card intent
),
merchant = "Demo Store",
merchantTransactionId = { UUID.randomUUID().toString() },
merchantTransactionDate = { Instant.now().toString() }
)Use this flow for:
- Digital products
- Simple orders with immediate fulfillment
- Subscriptions
- Donations
When a Google Pay payment succeeds, your onSuccess callback receives the same standard result as other payment methods:
onSuccess = { result: DropInSubmitResult ->
Log.d("Checkout", "Payment details:")
Log.d("Checkout", "- System transaction ID: ${result.systemTransactionId}")
Log.d("Checkout", "- Merchant transaction ID: ${result.merchantTransactionId}")
Log.d("Checkout", "- Payment method: ${result.paymentMethod}") // "GooglePay"
// Note: Amount, currency, and other transaction details must be retrieved from backend
// Google Pay tokenisation is handled internally
}Handle Google Pay-specific errors:
import com.pxp.checkout.exceptions.BaseSdkException
onError = { error: BaseSdkException ->
Log.e("Checkout", "Error code: ${error.code}")
Log.e("Checkout", "Error message: ${error.message}")
// Handle user cancellation
if (error.message?.contains("cancelled", ignoreCase = true) == true ||
error.message?.contains("closed", ignoreCase = true) == true) {
Log.d("Checkout", "User cancelled Google Pay payment")
// Don't show error - user intentionally cancelled
return@CheckoutDropInConfig
}
// Handle Google Pay-specific errors based on message content
when {
error.message?.contains("not available", ignoreCase = true) == true -> {
Toast.makeText(
context,
"Google Pay isn't available on this device. Please use a different payment method.",
Toast.LENGTH_LONG
).show()
}
error.message?.contains("not supported", ignoreCase = true) == true -> {
Toast.makeText(
context,
"Google Pay isn't supported for this transaction. Please use a different payment method.",
Toast.LENGTH_LONG
).show()
}
error.message?.contains("declined", ignoreCase = true) == true -> {
Toast.makeText(
context,
"Payment declined. Please try a different payment method.",
Toast.LENGTH_LONG
).show()
}
error.message?.contains("timeout", ignoreCase = true) == true -> {
Toast.makeText(
context,
"Google Pay timed out. Please try again.",
Toast.LENGTH_SHORT
).show()
}
else -> {
Toast.makeText(
context,
"Google Pay payment failed: ${error.message}",
Toast.LENGTH_LONG
).show()
}
}
}The following table describes common Google Pay error scenarios:
| Scenario | How to detect | Recommended action |
|---|---|---|
| User cancelled | error.message contains "cancelled" or "closed" | No alert needed - user action was intentional |
| Google Pay unavailable | error.message contains "not available" | Suggest using different payment method |
| Not supported | error.message contains "not supported" | Direct user to alternative payment options |
| Payment declined | error.message contains "declined" | Suggest trying different payment method |
| Timeout | error.message contains "timeout" | Suggest retrying the payment |
Google Pay errors return descriptive messages rather than specific error code constants. Check the error.message property for error details. You can also use the onCancel callback in methodConfig.global to specifically handle user cancellations.
Always verify Google Pay payments on your backend to ensure payment success before fulfilling orders:
import com.pxp.checkout.checkoutdropin.types.DropInSubmitResult
onSuccess = { result: DropInSubmitResult ->
// Send to backend for verification
coroutineScope.launch {
try {
val response = apiClient.post("/api/verify-payment") {
contentType(ContentType.Application.Json)
setBody(mapOf(
"systemTransactionId" to result.systemTransactionId,
"merchantTransactionId" to result.merchantTransactionId
))
}
if (response.status.value == 200) {
val verified = response.body<VerificationResponse>()
if (verified.success) {
// Navigate to success screen
navController.navigate("success?orderId=${verified.orderId}")
} else {
Toast.makeText(
context,
"Payment verification failed",
Toast.LENGTH_LONG
).show()
}
}
} catch (e: Exception) {
Log.e("Checkout", "Verification error", e)
Toast.makeText(
context,
"Failed to verify payment",
Toast.LENGTH_LONG
).show()
}
}
}Use the following backend code to verify Google Pay transactions via the PXP API:
// BACKEND: Verify Google Pay payment
app.post('/api/verify-payment', async (req, res) => {
const { systemTransactionId, merchantTransactionId } = req.body;
try {
// Query the PXP API to get transaction details
const txnPath = `api/v1/transactions/${systemTransactionId}`;
const { authHeader, requestId } = createAuthHeader(
txnPath,
'',
process.env.PXP_TOKEN_ID,
process.env.PXP_TOKEN_VALUE
);
const transaction = await fetch(
`https://api-services.pxp.io/${txnPath}`,
{
headers: {
'X-Client-Id': process.env.PXP_CLIENT_ID,
'X-Request-Id': requestId,
'Authorization': authHeader
}
}
).then(r => r.json());
// Verify transaction state
if (transaction.state !== 'Authorised' && transaction.state !== 'Captured') {
return res.json({ success: false, error: 'Transaction not successful' });
}
// Verify merchant transaction ID matches
if (transaction.merchantTransactionId !== merchantTransactionId) {
return res.json({ success: false, error: 'Transaction ID mismatch' });
}
// Verify amount matches expected amount from your order records
const order = await getOrderByMerchantTransactionId(merchantTransactionId);
const txnAmount = transaction.amounts?.transactionValue || transaction.amount || 0;
if (Math.abs(txnAmount - order.amount) > 0.01) {
return res.json({ success: false, error: 'Amount mismatch' });
}
// Google Pay payments show as Card funding type (processed as card transactions)
const fundingType = transaction.fundingData?.fundingType ||
transaction.fundingType ||
'Unknown';
if (fundingType !== 'Card') {
return res.json({ success: false, error: 'Invalid funding type' });
}
// Fulfill order
const orderId = await fulfillOrder(transaction);
return res.json({ success: true, orderId });
} catch (error) {
console.error('Verification error:', error);
return res.json({ success: false, error: 'Verification failed' });
}
});Use the following test cards in the Google Pay test environment:
| Card network | Test card number | Expiry | CVC |
|---|---|---|---|
| Visa | 4111 1111 1111 1111 | Any future date | Any 3 digits |
| Mastercard | 5555 5555 5555 4444 | Any future date | Any 3 digits |
| Amex | 3782 822463 10005 | Any future date | Any 4 digits |
To test Google Pay, you must add these test cards to your Google account in the test environment. Open Google Wallet on your test device and add the test cards manually.
Configure your app for Google Pay testing:
// Use test environment for Google Pay testing
CheckoutDropInConfig(
environment = Environment.TEST, // Test environment
// ... other config
methodConfig = DropInMethodConfig(
googlePay = DropInGooglePayConfig(
collectCvc = CollectCvcMode.DEFAULT,
billingAddressRequired = false,
shippingAddressRequired = false
)
)
)