The Google Pay button component emits events based on user interaction and payment processing stages. You can implement callback functions to inject your own business logic and customise the user experience at critical moments throughout the payment flow. While the SDK handles the complex technical aspects of payment processing, you retain full control over the customer experience and can seamlessly integrate payments into your broader business workflows.
Callbacks enable you to:
- Validate business rules before payments proceed.
- Display custom error, failure, or success messages.
- Integrate with fraud detection systems.
- Control the payment flow based on transaction data.
- Handle 3D Secure authentication.
- Collect and validate additional security data (CVC).
- Manage consent for payment tokens and recurring payments.
- Track analytics and monitor payment performance.
- Implement retry logic for soft declines.
This callback is triggered before payment authorisation, allowing you to provide transaction initialisation data or control whether to proceed.
You can use it to:
- Integrate with Kount or other fraud detection services.
- Apply business rules based on transaction amount or customer history.
- Check product availability before processing payment.
- Provide risk screening data for fraud prevention.
- Configure PSD2 SCA exemptions for the transaction.
- Cancel the payment if business rules aren't met.
onPreAuthorisation: (suspend (PreAuthorisationData?) -> GooglePayTransactionInitData?)?| Parameter | Description |
|---|---|
dataPreAuthorisationData? | Pre-authorisation data containing token identifiers. |
data.gatewayTokenIdString? | The gateway token identifier for the payment. Use this ID to retrieve full token details from the Unity backend. |
data.schemeTokenIdString? | The scheme token identifier for the payment (e.g., Visa Token Service, Mastercard Digital Enablement Service). |
| Return type | Description |
|---|---|
GooglePayTransactionInitData | Transaction initialisation data to proceed with payment. |
GooglePayTransactionInitData.psd2Data | PSD2 data including SCA exemption configuration. |
GooglePayTransactionInitData.psd2Data.scaExemption | SCA exemption type: ANONYMOUS_CARD, LOW_VALUE, SECURE_CORPORATE, TRANSACTION_RISK_ANALYSIS, TRUSTED_BENEFICIARY. |
GooglePayTransactionInitData.riskScreeningData | Risk screening data for fraud detection (Kount). |
GooglePayTransactionInitData.riskScreeningData.performRiskScreening | Whether to perform risk screening (Boolean). |
GooglePayTransactionInitData.riskScreeningData.excludeDeviceData | Whether to exclude device data from risk screening (Boolean). |
null | Return null to cancel the authorisation. |
import com.pxp.checkout.components.googlepay.types.GooglePayTransactionInitData
import com.pxp.checkout.services.kount.*
import com.pxp.checkout.services.models.transaction.ScaExemptionType
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onPreAuthorisation = suspend { data ->
Log.d("GooglePay", "Pre-authorisation started")
Log.d("GooglePay", "Gateway Token ID: ${data?.gatewayTokenId}")
Log.d("GooglePay", "Scheme Token ID: ${data?.schemeTokenId}")
// Perform pre-payment validation
val isHighRisk = checkCustomerRiskProfile()
val customerTier = getCustomerTier()
// Check inventory before proceeding
val inventoryAvailable = checkInventoryAvailability()
if (!inventoryAvailable) {
showError("Some items are no longer available")
return@suspend null // Cancel payment
}
// Check business rules
val cartTotal = getCurrentCartTotal()
if (cartTotal > 10000.0 && isHighRisk) {
showError("Transaction requires additional verification")
return@suspend null // Cancel payment for manual review
}
GooglePayTransactionInitData(
riskScreeningData = RiskScreeningData(
performRiskScreening = true,
excludeDeviceData = false,
userIp = "192.168.1.100",
account = RiskScreeningAccount(
id = "user_12345678",
creationDateTime = "2024-01-15T10:30:00.000Z"
),
transaction = RiskScreeningTransaction(
subtotal = cartTotal
),
items = cartItems.map { item ->
RiskScreeningItem(
price = item.price,
quantity = item.quantity,
category = item.category,
sku = item.sku
)
},
fulfillments = listOf(
RiskScreeningFulfillment(
type = FulfillmentType.SHIPPED,
shipping = RiskScreeningShipping(
shippingMethod = ShippingMethod.STANDARD
),
recipientPerson = RiskScreeningRecipientPerson(
phoneNumber = "+1234567890"
)
)
)
),
psd2Data = GooglePayTransactionInitData.Psd2Data(
scaExemption = if (cartTotal < 30.0) {
ScaExemptionType.LOW_VALUE
} else {
ScaExemptionType.TRANSACTION_RISK_ANALYSIS
}
)
)
}
}This callback is triggered after the payment is authorised, providing the transaction result and payment data.
You can use it to:
- Handle successful or declined payments.
- Navigate to success or failure screens.
- Update inventory and order status.
- Send order confirmation emails.
- Record transactions for business intelligence.
- Display custom success or error messages.
- Process shipping information collected from Google Pay.
- Save transaction details for customer account history.
- Update UI based on payment outcome.
onPostAuthorisation: ((SubmitResult?, AuthorisationPaymentData) -> Unit)?| Parameter | Description |
|---|---|
resultSubmitResult? | The transaction result: either MerchantSubmitResult (success) or FailedSubmitResult (failure). |
result.merchantTransactionIdString | Your unique transaction identifier (present in both success and failure). |
result.systemTransactionIdString | PXP system transaction identifier (only in MerchantSubmitResult). |
result.errorCodeString | Error code (only in FailedSubmitResult). |
result.errorReasonString | Error description (only in FailedSubmitResult). |
result.correlationIdString | Correlation ID for tracking (only in FailedSubmitResult). |
result.httpStatusCodeInt | HTTP status code (only in FailedSubmitResult). |
paymentDataAuthorisationPaymentData required | Google Pay payment data collected during checkout. |
paymentData.emailString? | Customer email address (if email collection was enabled). |
paymentData.shippingAddressMap<String, String?>? | Shipping address (if shipping address collection was enabled). |
paymentData.shippingOptionMap<String, String?>? | Selected shipping option (if shipping options were provided). |
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onPostAuthorisation = { result, paymentData ->
Log.d("GooglePay", "Payment completed: $result")
when (result) {
is MerchantSubmitResult -> {
// Payment successful
Log.d("GooglePay", "Payment authorised successfully")
Log.d("GooglePay", "Merchant Transaction ID: ${result.merchantTransactionId}")
Log.d("GooglePay", "System Transaction ID: ${result.systemTransactionId}")
// Extract payment data
val email = paymentData.email
val shippingAddress = paymentData.shippingAddress
val shippingOption = paymentData.shippingOption
// Use your Activity's lifecycleScope or CoroutineScope
lifecycleScope.launch {
try {
// Update inventory
updateInventory(result.merchantTransactionId)
// Create order record
createOrder(
transactionId = result.merchantTransactionId,
systemTransactionId = result.systemTransactionId,
email = email,
shippingAddress = shippingAddress,
shippingMethod = shippingOption?.get("id"),
status = "confirmed"
)
// Send confirmation email
sendConfirmationEmail(
transactionId = result.merchantTransactionId,
email = email,
shippingAddress = shippingAddress
)
// Track successful payment
analyticsTracker.trackEvent("google_pay_success") {
put("transaction_id", result.merchantTransactionId)
put("cart_value", getCurrentCartValue())
put("shipping_method", shippingOption?.get("id"))
}
// Update customer account
updateCustomerPurchaseHistory(result.merchantTransactionId)
// Navigate to success screen
withContext(Dispatchers.Main) {
navController.navigate(
"payment_success/${result.merchantTransactionId}"
)
}
} catch (e: Exception) {
Log.e("GooglePay", "Error in post-authorisation", e)
showError("Payment succeeded but order processing failed")
}
}
}
is FailedSubmitResult -> {
// Payment declined
Log.e("GooglePay", "Payment declined: ${result.errorReason}")
Log.e("GooglePay", "Error code: ${result.errorCode}")
Log.e("GooglePay", "Correlation ID: ${result.correlationId}")
// Log failure for monitoring
logPaymentFailure(
errorCode = result.errorCode,
errorReason = result.errorReason,
correlationId = result.correlationId,
merchantTransactionId = result.merchantTransactionId
)
// Show user-friendly error message
val errorMessage = getErrorMessage(result.errorCode)
showError("Payment declined: $errorMessage")
// Track failed payment
analyticsTracker.trackEvent("google_pay_declined") {
put("error_code", result.errorCode)
put("correlation_id", result.correlationId)
}
// Offer alternatives
showAlternativePaymentMethods()
}
else -> {
// Unexpected result
Log.e("GooglePay", "Unexpected result: $result")
showError("Payment processing error. Please contact support.")
analyticsTracker.trackEvent("google_pay_exception") {
put("result", result.toString())
}
}
}
}
}This callback is triggered when payment data changes in the Google Pay sheet, such as shipping address or shipping option selection.
You can use it to:
- Calculate shipping costs based on selected address.
- Update transaction amounts dynamically.
- Validate addresses and show errors for unserviceable locations.
- Add or remove line items based on selections.
- Calculate taxes based on shipping destination.
- Apply discounts based on shipping method selection.
- Check delivery availability for specific locations.
When updating transaction amounts, use the SDK's updateAmount() method to synchronise the amount with the backend session.
onPaymentDataChanged: ((Any) -> Any?)?| Parameter | Description |
|---|---|
intermediatePaymentDataAny required | Google Pay intermediate payment data containing shipping address and option selections. This is a dynamic object from the Google Pay API that you'll need to parse based on Google Pay's structure. |
The intermediatePaymentData object contains fields like callbackTrigger, shippingAddress, and shippingOptionData that follow Google Pay's API structure. You'll need to cast and parse this Any object to access these properties.
Return a Map structure representing updated payment data to display in the Google Pay sheet. The structure should follow Google Pay's PaymentDataRequestUpdate format with the following fields:
| Return field | Description |
|---|---|
newTransactionInfo | Map containing updated transaction information. |
newTransactionInfo.totalPrice | Updated total price as a string. |
newTransactionInfo.totalPriceStatus | Price status: "FINAL" or "ESTIMATED". |
newTransactionInfo.displayItems | List of Maps representing line items with label, type, and price fields. |
newShippingOptionParameters | Map containing updated shipping options (optional). |
error | Map containing error information for invalid addresses or options (optional). |
error.reason | Error reason: "SHIPPING_ADDRESS_INVALID", "SHIPPING_ADDRESS_UNSERVICEABLE", "SHIPPING_OPTION_INVALID". |
error.message | User-friendly error message. |
error.intent | The callback intent that triggered the error: "SHIPPING_ADDRESS", "SHIPPING_OPTION". |
val googlePayConfig = GooglePayButtonComponentConfig().apply {
paymentDataRequest = PaymentDataRequest(
// ... other configuration
shippingAddressRequired = true,
shippingOptionRequired = true
)
onPaymentDataChanged = { intermediatePaymentData ->
Log.d("GooglePay", "Payment data changed")
// Parse the intermediate payment data
// Note: You'll need to cast/parse based on Google Pay's structure
val baseAmount = 20.00
var tax = 5.00
var shippingCost = 0.0
var errorMap: Map<String, String>? = null
// Example: Handle shipping address or option changes
// The structure depends on Google Pay's IntermediatePaymentData format
// Calculate new total
val newTotal = baseAmount + tax + shippingCost
// Update SDK amount
pxpCheckout.updateAmount(newTotal)
// Return updated payment data as Map
mapOf(
"newTransactionInfo" to mapOf(
"totalPrice" to newTotal.toString(),
"totalPriceStatus" to "FINAL",
"displayItems" to listOf(
mapOf(
"label" to "Subtotal",
"type" to "SUBTOTAL",
"price" to baseAmount.toString()
),
mapOf(
"label" to "Shipping",
"type" to "LINE_ITEM",
"price" to shippingCost.toString()
),
mapOf(
"label" to "Tax",
"type" to "TAX",
"price" to tax.toString()
)
)
),
"error" to errorMap // Include error if validation failed
).filterValues { it != null } // Remove null values
}
}This callback is triggered when the Google Pay button is tapped, before the payment sheet opens.
You can use it to:
- Track button click events for analytics.
- Show loading indicators or progress dialogs.
- Perform final validation before opening payment sheet.
- Update cart totals or recalculate amounts.
- Check inventory availability one last time.
- Prepare data for the payment flow.
onGooglePaymentButtonClicked: (() -> kotlinx.coroutines.Deferred<Unit>?)?This callback receives no parameters.
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onGooglePaymentButtonClicked = {
Log.d("GooglePay", "Google Pay button clicked")
// Track button click
analyticsTracker.trackEvent("google_pay_button_clicked") {
put("timestamp", System.currentTimeMillis())
put("cart_value", getCurrentCartValue())
put("item_count", getCartItemCount())
}
// Show loading indicator
withContext(Dispatchers.Main) {
showLoadingDialog()
}
// Final inventory check
val inventoryOk = checkInventoryAvailability()
if (!inventoryOk) {
withContext(Dispatchers.Main) {
hideLoadingDialog()
showError("Some items are no longer available")
}
throw IllegalStateException("Inventory unavailable")
}
// Prepare checkout data
prepareCheckoutData()
// Update cart totals
recalculateCartTotals()
withContext(Dispatchers.Main) {
hideLoadingDialog()
}
CompletableDeferred(Unit)
}
}This callback is executed before opening the Google Pay payment sheet, allowing custom validation of other form fields.
You can use it to:
- Validate required form fields before payment.
- Check terms and conditions acceptance.
- Verify customer information completeness.
- Prevent payment sheet from opening if validation fails.
- Validate minimum order amounts.
- Check age verification requirements.
onCustomValidation: (() -> suspend Boolean)?This callback receives no parameters.
| Return value | Description |
|---|---|
Boolean | Return true to proceed with opening payment sheet, false to prevent opening. |
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onCustomValidation = suspend {
var isValid = true
// Validate email
val email = emailField.text.toString()
if (email.isEmpty() || !email.contains('@')) {
showError("Please enter a valid email address")
highlightField(emailField)
isValid = false
}
// Validate phone number (if required)
val phone = phoneField.text.toString()
if (phone.isEmpty() || phone.length < 10) {
showError("Please enter a valid phone number")
highlightField(phoneField)
isValid = false
}
// Validate terms acceptance
val termsAccepted = termsCheckbox.isChecked
if (!termsAccepted) {
showError("Please accept the terms and conditions")
highlightField(termsCheckbox)
isValid = false
}
// Validate age verification (if required)
val ageVerified = ageVerifyCheckbox.isChecked
if (!ageVerified) {
showError("You must be 18 or older to complete this purchase")
isValid = false
}
// Validate minimum order amount
val cartTotal = getCurrentCartTotal()
if (cartTotal < 10.00) {
showError("Minimum order amount is £10.00")
isValid = false
}
// Final inventory check
if (isValid) {
val inventoryAvailable = checkInventory()
if (!inventoryAvailable) {
showError("Some items are no longer available. Please review your cart.")
isValid = false
}
}
isValid // Return validation result
}
}This callback is triggered when an error occurs during the Google Pay payment process.
You can use it to:
- Log errors for debugging and monitoring.
- Display user-friendly error messages.
- Offer alternative payment methods.
- Implement automatic retry for transient errors.
- Send error notifications to support teams.
- Track error rates for quality monitoring.
onError: ((BaseSdkException) -> Unit)?| Event data | Description |
|---|---|
errorBaseSdkException required | The SDK exception containing error details. |
error.messageString? | A human-readable error description. |
error.ErrorCodeString | The error code for programmatic handling. |
error.causeThrowable? | The underlying cause of the error, if available. |
| Exception | Code | Description |
|---|---|---|
GooglePayNotReadyException | SDK0701 | Google Pay not available on device. |
GooglePayPaymentFailedException | SDK0702 | Payment processing failed. |
GooglePayClientNotInitializedException | SDK0703 | Client not properly initialised. |
GooglePayLoadFailedException | SDK0704 | Failed to load Google Pay API. |
GooglePayConfigurationValidationFailedException | SDK0705 | Invalid configuration. |
GooglePayDecryptAndTokenVaultFailedException | SDK0706 | Token processing failed. |
UnsupportedFundingTypeGooglePaySdkException | SDK0110 | Google Pay not in allowed funding types. |
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onError = { error ->
Log.e("GooglePay", "Error occurred", error)
// Log error for debugging
logError("google-pay-error") {
put("message", error.message ?: "Unknown error")
put("code", error.ErrorCode)
put("timestamp", System.currentTimeMillis())
put("user_id", getCurrentUserId())
}
// Handle specific error types
when (error) {
is GooglePayNotReadyException -> {
Log.e("GooglePay", "Google Pay not available")
showError("Google Pay is not available on this device. Please use another payment method.")
showAlternativePaymentMethods()
analyticsTracker.trackEvent("google_pay_not_available")
}
is GooglePayConfigurationValidationFailedException -> {
Log.e("GooglePay", "Configuration validation failed")
showError("Payment system temporarily unavailable. Please try again later.")
notifySupport("Configuration error", error)
}
is GooglePayClientNotInitializedException -> {
Log.e("GooglePay", "Client not initialised")
showError("Payment system not ready. Please refresh and try again.")
notifySupport("Client initialisation error", error)
}
is GooglePayLoadFailedException -> {
Log.e("GooglePay", "Failed to load Google Pay")
showError("Unable to load payment system. Please check your connection.")
offerRetryOption()
}
is GooglePayPaymentFailedException -> {
Log.e("GooglePay", "Payment processing failed")
showError("Payment failed. Please check your payment information.")
offerRetryOption()
}
is GooglePayDecryptAndTokenVaultFailedException -> {
Log.e("GooglePay", "Token vault error")
showError("Payment processing error. Please try again.")
notifySupport("Token vault error", error)
}
else -> {
Log.e("GooglePay", "Unexpected error: ${error.message}")
showError("An unexpected error occurred. Please try again.")
analyticsTracker.trackEvent("google_pay_error") {
put("error_type", error::class.simpleName ?: "Unknown")
put("error_code", error.ErrorCode)
}
}
}
}
}This callback is triggered when the customer cancels the Google Pay payment flow.
You can use it to:
- Track cancellation rates for conversion optimisation.
- Show helpful messages or alternative options.
- Save the customer's cart for later completion.
- Trigger abandoned cart workflows.
- Understand drop-off points in the payment flow.
- Offer assistance or support.
onCancel: (() -> Unit)?This callback receives no parameters.
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onCancel = {
Log.d("GooglePay", "Payment cancelled by user")
// Track cancellation
analyticsTracker.trackEvent("google_pay_cancelled") {
put("timestamp", System.currentTimeMillis())
put("cart_value", getCurrentCartValue())
put("item_count", getCartItemCount())
}
// Preserve cart for later
saveCartForLater()
// Show helpful message
showMessage(
"No worries! Your items are saved. You can complete your purchase anytime.",
isError = false
)
// Offer support for high-value carts
if (getCartValue() > 100.0) {
showSupportDialog("Need help completing your purchase?")
}
// Schedule abandoned cart notification
lifecycleScope.launch {
scheduleAbandonedCartNotification(
customerId = getCurrentCustomerId(),
cartData = getCurrentCartData(),
delayMinutes = 60
)
}
}
}This configuration parameter controls which authentication strategy is used for 3DS authentication.
useUnityAuthenticationStrategy: Boolean = falseWhen true, uses Unity's automatic authentication strategy. When false or not specified, uses manual authentication callbacks.
val googlePayConfig = GooglePayButtonComponentConfig().apply {
// Enable Unity authentication strategy
useUnityAuthenticationStrategy = true
// Simplified callbacks when using Unity strategy
onPreInitiateAuthentication = {
PreInitiateIntegratedAuthenticationData(
providerId = "pxpfinancial",
timeout = 12
)
}
onPreAuthentication = suspend {
InitiateIntegratedAuthenticationData(
merchantCountryNumericCode = "840",
merchantLegalName = "Your Store Name",
challengeWindowSize = ChallengeWindowSize.FULL_SCREEN,
requestorChallengeIndicator = RequestorChallengeIndicatorType.NO_PREFERENCE,
timeout = 180
)
}
}This callback is triggered before initiating the 3DS pre-authentication check.
You can use it to:
- Configure 3DS provider settings.
- Set authentication request preferences.
- Specify timeout values for authentication requests.
- Control the initial authentication flow.
onPreInitiateAuthentication: (() -> PreInitiateIntegratedAuthenticationData?)?This callback receives no parameters.
| Return value | Description |
|---|---|
PreInitiateIntegratedAuthenticationData | Configuration for 3DS pre-authentication. |
PreInitiateIntegratedAuthenticationData.providerId | Your 3DS provider identifier. Optional. |
PreInitiateIntegratedAuthenticationData.requestorAuthenticationIndicator | The authentication indicator. Optional. Possible values:
|
PreInitiateIntegratedAuthenticationData.timeout | The timeout to get the fingerprint result, in seconds (max 600). Optional. |
null | Return null to skip 3DS authentication. |
import com.pxp.checkout.services.models.authentication.PreInitiateIntegratedAuthenticationData
import com.pxp.checkout.models.RequestorAuthenticationIndicatorType
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onPreInitiateAuthentication = {
Log.d("GooglePay", "Initiating 3DS pre-authentication")
// Track 3DS initiation
analyticsTracker.trackEvent("3ds_pre_initiate")
PreInitiateIntegratedAuthenticationData(
providerId = "your-3ds-provider-id", // Optional
requestorAuthenticationIndicator = RequestorAuthenticationIndicatorType.PAYMENT_TRANSACTION,
timeout = 300 // 5 minutes
)
}
}This callback is triggered after the 3DS pre-authentication check completes.
You can use it to:
- Read
authenticationIdandthreeDSecureSupportedfrom the success result. - Retrieve authentication assessment results from your backend using the
authenticationId. - Determine if full authentication is required.
- Log authentication initiation for compliance.
- Check SCA requirements and exemptions.
- Decide whether to proceed with challenge.
onPostInitiateAuthentication: ((AuthenticationResult) -> Unit)?| Parameter | Description |
|---|---|
resultAuthenticationResult | Authentication initiation result. Success is MerchantAuthenticationResult; failure is FailedAuthenticationResult. |
result.authenticationIdString? | Unique identifier for this 3DS session. May be null when 3DS isn't required. Use this to retrieve full pre-authentication results from your backend. |
result.threeDSecureSupportedBoolean | Whether 3DS authentication is supported for this transaction. |
When both authenticationId is absent and threeDSecureSupported is false, the SDK skips the remaining 3DS flow and doesn't invoke onPreAuthentication. This early-exit check applies when useUnityAuthenticationStrategy is false. When useUnityAuthenticationStrategy is true, the SDK uses Unity's authentication strategy API instead. Use the authenticationId with the Get 3DS pre-initiate authentication details API to retrieve SCA mandates, exemptions, and state.
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onPostInitiateAuthentication = { result ->
when (result) {
is FailedAuthenticationResult -> {
Log.e("GooglePay", "3DS pre-initiation failed: ${result.errorReason}")
}
is MerchantAuthenticationResult -> {
Log.d("GooglePay", "Pre-authentication complete: ${result.authenticationId}")
Log.d("GooglePay", "3DS supported: ${result.threeDSecureSupported}")
if (result.authenticationId.isNullOrEmpty() && !result.threeDSecureSupported) {
return@onPostInitiateAuthentication
}
val authId = result.authenticationId ?: return@onPostInitiateAuthentication
lifecycleScope.launch {
try {
val authResult = fetchAuthenticationResultFromBackend(authId)
if (authResult.state == "PendingClientData") {
Log.d("GooglePay", "Waiting for client data collection")
}
if (authResult.scaMandated) {
Log.d("GooglePay", "SCA is required - 3DS must complete successfully")
withContext(Dispatchers.Main) {
showMessage("Additional verification required")
}
}
evaluateAuthenticationAndUpdateSession(authResult)
} catch (e: Exception) {
Log.e("GooglePay", "Error retrieving authentication result", e)
}
}
}
}
}
}This callback is triggered before full 3DS authentication begins, allowing you to provide authentication configuration.
You can use it to:
- Specify merchant details for authentication.
- Configure challenge window preferences.
- Set challenge indicators based on risk assessment.
- Provide country-specific merchant information.
- Control the authentication user experience.
onPreAuthentication: (suspend () -> InitiateIntegratedAuthenticationData?)?This callback receives no parameters.
| Return value | Description |
|---|---|
InitiateIntegratedAuthenticationData | Configuration for 3DS authentication. |
InitiateIntegratedAuthenticationData.merchantCountryNumericCode | Your merchant location, as an ISO numeric country code (e.g., "840" for USA, "826" for UK). Optional. |
InitiateIntegratedAuthenticationData.merchantLegalName | Your legal business name. Optional. |
InitiateIntegratedAuthenticationData.challengeWindowSize | The challenge window size. Optional. Possible values:
|
InitiateIntegratedAuthenticationData.requestorChallengeIndicator | The challenge preference indicator. Optional. Possible values:
|
InitiateIntegratedAuthenticationData.timeout | Timeout to get the challenge result, in seconds (max 600). Optional. |
InitiateIntegratedAuthenticationData.billingAddress | Billing address for authentication. Optional. |
InitiateIntegratedAuthenticationData.shopper | Shopper information. Optional. |
null | Return null to skip authentication. |
import com.pxp.checkout.services.models.authentication.InitiateIntegratedAuthenticationData
import com.pxp.checkout.models.RequestorChallengeIndicatorType
import com.pxp.checkout.components.threeds.ChallengeWindowSize
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onPreAuthentication = suspend {
Log.d("GooglePay", "Configuring 3DS authentication")
// Get authentication decision from backend
val decision = getAuthenticationDecision()
if (decision == null) {
Log.d("GooglePay", "Skipping authentication based on backend decision")
return@suspend null
}
// Assess transaction risk
val amount = getTransactionAmount()
val customerHistory = getCustomerHistory()
val isHighRisk = amount > 500.0 || customerHistory.chargebacks > 0
// Determine challenge indicator
val challengeIndicator = when {
isHighRisk -> RequestorChallengeIndicatorType.CHALLENGE_REQUESTED_MANDATE
amount < 30.0 && customerHistory.successfulTransactions > 10 ->
RequestorChallengeIndicatorType.NO_CHALLENGE_REQUESTED
else -> RequestorChallengeIndicatorType.NO_PREFERENCE
}
Log.d("GooglePay", "Challenge indicator: $challengeIndicator")
InitiateIntegratedAuthenticationData(
merchantCountryNumericCode = "826", // United Kingdom
merchantLegalName = "Your Store Limited",
challengeWindowSize = ChallengeWindowSize.FULL_SCREEN,
requestorChallengeIndicator = challengeIndicator,
timeout = if (decision.scaMandated) 600 else 300,
billingAddress = getBillingAddress()?.let {
BillingAddress(
city = it.city,
countryNumericCode = it.countryCode,
line1 = it.streetAddress,
postalCode = it.postalCode
)
},
shopper = ShopperData(
email = getCurrentCustomerEmail(),
homePhoneNumber = getCurrentCustomerPhone()
)
)
}
}This callback is triggered after 3DS authentication completes, providing the authentication result.
You can use it to:
- Retrieve authentication results.
- Log authentication outcomes for compliance.
- Track authentication success rates.
- Handle authentication failures gracefully.
- Record ECI values and authentication indicators.
onPostAuthentication: ((AuthenticationResult) -> Unit)?| Parameter | Description |
|---|---|
dataMerchantAuthenticationResult required | Authentication result containing the authentication ID. |
data.authenticationIdString required | Unique identifier for the authentication attempt. Use this to retrieve full authentication details from your backend. |
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onPostAuthentication = { data ->
Log.d("GooglePay", "3DS authentication completed: ${data.authenticationId}")
lifecycleScope.launch {
try {
// Retrieve authentication result from backend
val authResult = fetchAuthenticationResultFromBackend(data.authenticationId)
Log.d("GooglePay", "Authentication result: $authResult")
// Check transaction status
when (authResult.transactionStatus) {
"Y" -> {
Log.d("GooglePay", "Authentication successful - no challenge needed")
showMessage("Card verified successfully")
}
"C" -> {
if (authResult.state == "AuthenticationSuccessful") {
Log.d("GooglePay", "Challenge completed successfully")
showMessage("Verification completed")
} else {
Log.e("GooglePay", "Challenge failed")
showError("Verification failed. Please try again.")
return@launch // Stop payment
}
}
"N" -> {
Log.e("GooglePay", "Authentication failed")
showError("Card verification failed")
return@launch // Stop payment
}
"R" -> {
Log.e("GooglePay", "Authentication rejected")
showError("Payment was rejected by your bank")
return@launch // Stop payment
}
}
// Evaluate and update authorisation decision
evaluateAuthenticationAndUpdateAuthorization(authResult)
Log.d("GooglePay", "Proceeding to final authorisation...")
} catch (e: Exception) {
Log.e("GooglePay", "Error processing authentication result", e)
showError("Authentication processing error")
}
}
}
}This callback is called to determine if the customer has given consent to store their payment token for future use.
The primary use case is to set a default consent value (true or false) when you're not rendering the google-pay-consent checkbox component. This allows you to control token storage behaviour without requiring explicit customer interaction.
When both googlePayConsentComponent and onGetConsent are provided, the consent component value takes priority. The onGetConsent callback will not be called if a consent component is linked.
You can use it to:
- Set a default consent value when not using the consent checkbox component.
- Check custom consent checkbox status from your own UI.
- Verify terms and conditions acceptance for stored payments.
- Control token storage behaviour based on business logic.
- Implement opt-in/opt-out for recurring payments.
- Respect customer privacy preferences.
onGetConsent: (() -> Boolean)?This callback receives no parameters.
| Return Type | Description |
|---|---|
Booleanrequired | Return true if consent was given, false otherwise. |
Example 1: Set default consent value without checkbox
val googlePayConfig = GooglePayButtonComponentConfig().apply {
// ... payment configuration
onGetConsent = {
// Return a default value when not using consent checkbox
// Set to true to always store tokens, false to never store
Log.d("GooglePay", "Setting default consent: true")
true // Always save payment method for this merchant
}
onPostAuthorisation = { result, paymentData ->
when (result) {
is MerchantSubmitResult -> {
Log.d("GooglePay", "Payment successful, token will be stored")
// Token storage is handled automatically based on consent
}
else -> { /* Handle failure */ }
}
}
}Example 2: Use custom consent checkbox from your UI
val googlePayConfig = GooglePayButtonComponentConfig().apply {
// ... payment configuration
onGetConsent = {
// Check your own custom checkbox
val consentGiven = customConsentCheckbox.isChecked
if (consentGiven) {
Log.d("GooglePay", "Customer consented to save payment method")
// Track consent
analyticsTracker.trackEvent("payment_consent_given") {
put("timestamp", System.currentTimeMillis())
put("payment_method", "google-pay")
}
} else {
Log.d("GooglePay", "Customer did not consent to save payment method")
}
consentGiven
}
}Example 3: With consent component (component value takes priority)
// Create consent component
val consentComponent = pxpCheckout.createComponent<
GooglePayConsentComponent,
GooglePayConsentConfig
>(
type = ComponentType.GOOGLE_PAY_CONSENT,
config = GooglePayConsentConfig(
label = "Save this payment method for future purchases",
initialChecked = false
)
)
// Create Google Pay button with consent
val googlePayConfig = GooglePayButtonComponentConfig().apply {
// ... payment configuration
// Link the consent component
googlePayConsentComponent = consentComponent
// This callback will NOT be called because consent component is linked
onGetConsent = {
Log.d("GooglePay", "This callback is ignored when component is linked")
true // This is ignored when consent component is present
}
}
// The consent component's checked state will be used for token storageThe Google Pay Button Component can request CVV/CVC codes for additional security. While there's no specific callback for CVC collection, you can configure the behaviour:
val googlePayConfig = GooglePayButtonComponentConfig().apply {
// CVC collection strategy
collectCvc = GooglePayButtonComponentConfig.CvcCollectionStrategy.DEFAULT
// Custom CVC dialog configuration (optional)
cvcVerificationPopupConfig = CvcVerificationDialogConfig(
title = "Security Verification",
description = "Please enter your card's CVV code",
submitButtonText = "Confirm Payment",
cancelButtonText = "Go Back",
// Custom styling
titleStyles = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF1A237E)
),
// CVC input configuration
cvcComponentConfig = CardCvcComponentConfig().apply {
label = "CVV"
showHintIcon = true
showValidIcon = true
showMaskToggle = true
}
)
}enum class CvcCollectionStrategy {
ALWAYS, // Always show CVC dialog for FPAN
NEVER, // Never show CVC dialog
DEFAULT // Show CVC only when 3DS not available/skipped
}This callback is triggered when Kount fraud detection data collection begins.
You can use it to:
- Track fraud detection start time.
- Show loading indicators during fraud screening.
- Log fraud detection events.
- Monitor fraud detection performance.
onKountCollectStart: ((Any) -> Unit)?| Parameter | Description |
|---|---|
dataAny | Kount collection start data. |
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onKountCollectStart = { data ->
Log.d("GooglePay", "Kount fraud detection started")
// Track fraud detection start
analyticsTracker.trackEvent("kount_collection_start") {
put("timestamp", System.currentTimeMillis())
}
// Show fraud check indicator (optional)
lifecycleScope.launch(Dispatchers.Main) {
showFraudCheckIndicator()
}
}
}This callback is triggered when Kount fraud detection data collection completes.
You can use it to:
- Track fraud detection completion time.
- Hide loading indicators.
- Log fraud detection completion.
- Calculate fraud detection duration.
onKountCollectEnd: ((Any) -> Unit)?| Parameter | Description |
|---|---|
dataAny | Kount collection completion data. |
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onKountCollectStart = { data ->
val startTime = System.currentTimeMillis()
// Store start time for duration calculation
fraudDetectionStartTime = startTime
Log.d("GooglePay", "Kount collection started at $startTime")
}
onKountCollectEnd = { data ->
val endTime = System.currentTimeMillis()
val duration = endTime - fraudDetectionStartTime
Log.d("GooglePay", "Kount collection completed in ${duration}ms")
// Track fraud detection completion
analyticsTracker.trackEvent("kount_collection_end") {
put("timestamp", endTime)
put("duration_ms", duration)
}
// Hide fraud check indicator
lifecycleScope.launch(Dispatchers.Main) {
hideFraudCheckIndicator()
}
}
}
private var fraudDetectionStartTime: Long = 0This callback is triggered before retrying a soft-declined payment with different authentication parameters.
You can use it to:
- Configure retry attempts with alternative challenge indicators.
- Implement custom retry logic for soft declines.
- Track soft decline occurrences.
- Adjust authentication parameters for better success rates.
- Decide whether to retry or abort.
onPreRetrySoftDecline: ((SubmitResult) -> RetryConfiguration?)?| Parameter | Description |
|---|---|
resultSubmitResult required | Transaction result containing decline information. |
result.errorCodeString | Error code indicating decline type. |
result.errorReasonString | Human-readable decline reason. |
result.correlationIdString | Correlation ID for tracking. |
result.merchantTransactionIdString | Your transaction identifier. |
result.systemTransactionIdString? | System transaction identifier (if available). |
| Property | Description |
|---|---|
retryBoolean required | Whether to retry the payment. |
updatedConfigsUpdatedConfigs? | Updated callback configurations for retry attempt. Contains optional onPreInitiateAuthentication, onPreAuthentication, and onPreAuthorisation callbacks. |
import com.pxp.checkout.components.googlepay.types.GooglePayButtonComponentConfig
import com.pxp.checkout.services.models.authentication.*
import com.pxp.checkout.models.*
import com.pxp.checkout.components.threeds.ChallengeWindowSize
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onPreRetrySoftDecline = { result ->
Log.w("GooglePay", "Soft decline detected, preparing retry")
Log.w("GooglePay", "Error code: ${result.errorCode}")
Log.w("GooglePay", "Decline reason: ${result.errorReason}")
Log.w("GooglePay", "Correlation ID: ${result.correlationId}")
// Track soft decline
analyticsTracker.trackEvent("payment_soft_decline") {
put("error_code", result.errorCode)
put("error_reason", result.errorReason)
put("correlation_id", result.correlationId)
put("transaction_id", result.merchantTransactionId)
}
// Show message to user
showMessage("Processing payment with additional verification...")
// Retry with forced 3DS challenge
GooglePayButtonComponentConfig.RetryConfiguration(
retry = true,
updatedConfigs = GooglePayButtonComponentConfig.RetryConfiguration.UpdatedConfigs(
onPreInitiateAuthentication = {
PreInitiateIntegratedAuthenticationData(
providerId = "pxpfinancial",
timeout = 12
)
},
onPreAuthentication = suspend {
InitiateIntegratedAuthenticationData(
merchantCountryNumericCode = "826",
merchantLegalName = "Your Store Ltd",
challengeWindowSize = ChallengeWindowSize.FULL_SCREEN,
requestorChallengeIndicator =
RequestorChallengeIndicatorType.CHALLENGE_REQUESTED_MANDATE,
timeout = 300
)
}
)
)
// Alternative: Don't retry
// GooglePayButtonComponentConfig.RetryConfiguration(retry = false)
}
}Here's a comprehensive example showing multiple callbacks working together:
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.pxp.PxpCheckout
import com.pxp.checkout.components.googlepay.*
import com.pxp.checkout.components.googlepay.types.*
import com.pxp.checkout.models.*
import com.pxp.checkout.services.kount.*
import com.pxp.checkout.types.ComponentType
import kotlinx.coroutines.CompletableDeferred
@Composable
fun GooglePayPaymentScreen(
pxpCheckout: PxpCheckout,
amount: Double,
onSuccess: (String) -> Unit,
onError: (String) -> Unit
) {
var errorMessage by remember { mutableStateOf<String?>(null) }
var isProcessing by remember { mutableStateOf(false) }
val googlePayComponent = remember {
pxpCheckout.createComponent<
GooglePayButtonComponent,
GooglePayButtonComponentConfig
>(
type = ComponentType.GOOGLE_PAY_BUTTON,
config = GooglePayButtonComponentConfig().apply {
// Payment configuration
style = GooglePayButtonStyle(
type = GooglePayButtonType.PAY,
theme = GooglePayButtonTheme.DARK,
cornerRadius = 8
)
paymentDataRequest = PaymentDataRequest(
allowedPaymentMethods = listOf(
PaymentMethodSpecification(
parameters = PaymentMethodParameters(
allowedAuthMethods = listOf(
CardAuthMethod.PAN_ONLY,
CardAuthMethod.CRYPTOGRAM_3DS
),
allowedCardNetworks = listOf(
CardNetwork.VISA,
CardNetwork.MASTERCARD,
CardNetwork.AMEX
)
)
)
),
transactionInfo = TransactionInfo(
currencyCode = "GBP",
totalPriceStatus = TotalPriceStatus.FINAL,
totalPrice = amount.toString(),
countryCode = "GB"
),
emailRequired = true
)
// Enable 3DS with Unity strategy
useUnityAuthenticationStrategy = true
collectCvc = GooglePayButtonComponentConfig.CvcCollectionStrategy.DEFAULT
// Button click callback
onGooglePaymentButtonClicked = {
Log.d("GooglePay", "Button clicked")
isProcessing = true
errorMessage = null
CompletableDeferred(Unit)
}
// Pre-authorisation callback
onPreAuthorisation = suspend { data ->
Log.d("GooglePay", "Pre-authorisation: ${data?.gatewayTokenId}")
GooglePayTransactionInitData(
riskScreeningData = RiskScreeningData(
performRiskScreening = true,
excludeDeviceData = false,
userIp = "192.168.1.100",
account = RiskScreeningAccount(
id = "user_12345678",
creationDateTime = "2024-01-15T10:30:00.000Z"
),
fulfillments = listOf(
RiskScreeningFulfillment(
type = FulfillmentType.SHIPPED,
recipientPerson = RiskScreeningRecipientPerson(
phoneNumber = "+1234567890"
)
)
)
)
)
}
// Post-authorisation callback
onPostAuthorisation = { result, paymentData ->
isProcessing = false
when (result) {
is MerchantSubmitResult -> {
Log.d("GooglePay", "Payment successful")
onSuccess(result.merchantTransactionId)
}
is FailedSubmitResult -> {
Log.e("GooglePay", "Payment failed: ${result.errorReason}")
errorMessage = result.errorReason
onError(result.errorReason)
}
}
}
// Error callback
onError = { error ->
Log.e("GooglePay", "Error: ${error.message}", error)
isProcessing = false
errorMessage = error.message
onError(error.message ?: "Payment error")
}
// Cancel callback
onCancel = {
Log.d("GooglePay", "Payment cancelled")
isProcessing = false
errorMessage = "Payment cancelled"
}
}
)
}
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Complete Your Payment",
style = MaterialTheme.typography.headlineMedium
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "£${amount}",
style = MaterialTheme.typography.displaySmall
)
Spacer(modifier = Modifier.height(24.dp))
// Render Google Pay button
googlePayComponent?.Content(
modifier = Modifier.fillMaxWidth()
)
// Error message
errorMessage?.let { error ->
Spacer(modifier = Modifier.height(16.dp))
Text(
text = error,
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodyMedium
)
}
// Processing indicator
if (isProcessing) {
Spacer(modifier = Modifier.height(16.dp))
CircularProgressIndicator()
}
}
}The SDK automatically tracks key events throughout the Google Pay payment flow. These analytics events help you monitor payment performance, identify issues, and optimise the user experience.
The following events are tracked automatically without any additional configuration:
| Event | Description |
|---|---|
GooglePayButtonClickAnalyticsEvent | Triggered when the user taps the Google Pay button. Helps track button engagement and conversion funnel entry. |
GooglePayReadinessCheckAnalyticsEvent | Triggered when the SDK checks if Google Pay is available on the device. Useful for understanding device compatibility. |
GooglePaySheetOpenedAnalyticsEvent | Triggered when the Google Pay payment sheet opens. Indicates successful button interaction and sheet loading. |
GooglePaySheetCompletedAnalyticsEvent | Triggered when the Google Pay payment sheet closes (either completed or cancelled). Helps track sheet completion rates. |
GooglePayTokenReceivedAnalyticsEvent | Triggered when a payment token is successfully received from Google Pay. Indicates successful tokenisation. |
GooglePayFlowCancelledAnalyticsEvent | Triggered when the user cancels the payment flow. Helps identify drop-off points and cancellation patterns. |
ComponentErrorAnalyticsEvent | Triggered when an error occurs during the payment flow. Essential for monitoring error rates and types. |
Each analytics event contains:
- Event name: The type of event that occurred.
- Timestamp: When the event was triggered.
- Component type: Always
GOOGLE_PAY_BUTTONfor Google Pay events. - Additional context: Event-specific data (e.g., error details, token information).
Analytics events are automatically sent to the PXP backend and can be viewed in the Unity Portal analytics dashboard. You can use this data to:
- Monitor conversion rates: Track how many users complete payments after clicking the button.
- Identify technical issues: Detect patterns in error events and readiness check failures.
- Optimise user experience: Understand where users drop off in the payment flow.
- Track performance metrics: Measure average time from button click to payment completion.
If you want to integrate with your own analytics platform (e.g., Firebase Analytics, Google Analytics), you can track events using the callback functions:
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
val analytics = Firebase.analytics
val googlePayConfig = GooglePayButtonComponentConfig().apply {
onGooglePaymentButtonClicked = {
// Track button click
analytics.logEvent("google_pay_button_clicked", Bundle().apply {
putLong("timestamp", System.currentTimeMillis())
putString("currency", "GBP")
putDouble("amount", 99.99)
})
CompletableDeferred(Unit)
}
onPostAuthorisation = { result, paymentData ->
when (result) {
is MerchantSubmitResult -> {
// Track successful payment
analytics.logEvent("google_pay_payment_success", Bundle().apply {
putString("transaction_id", result.merchantTransactionId)
putString("system_transaction_id", result.systemTransactionId)
})
}
is FailedSubmitResult -> {
// Track failed payment
analytics.logEvent("google_pay_payment_failed", Bundle().apply {
putString("error_code", result.errorCode)
putString("error_reason", result.errorReason)
putString("correlation_id", result.correlationId)
})
}
}
}
onError = { error ->
// Track errors
analytics.logEvent("google_pay_error", Bundle().apply {
putString("error_type", error::class.simpleName)
putString("error_message", error.message)
})
}
onCancel = {
// Track cancellations
analytics.logEvent("google_pay_cancelled", Bundle().apply {
putLong("timestamp", System.currentTimeMillis())
})
}
}