# Configuration

Learn how to configure the Google Pay button component for Android with Kotlin and Jetpack Compose.

## Basic usage

### Minimal configuration

At minimum, the Google Pay button component requires the following configuration to function:

The SDK automatically configures tokenisation with the correct gateway and merchant ID from your session. You only need to provide `allowedPaymentMethods` and `transactionInfo`.

```kotlin
import com.pxp.checkout.components.googlepay.types.*

val config = GooglePayButtonComponentConfig().apply {
    paymentDataRequest = PaymentDataRequest(
        allowedPaymentMethods = listOf(
            PaymentMethodSpecification(
                parameters = PaymentMethodParameters(
                    allowedCardNetworks = listOf(
                        CardNetwork.VISA,
                        CardNetwork.MASTERCARD
                    ),
                    allowedAuthMethods = listOf(
                        CardAuthMethod.PAN_ONLY,
                        CardAuthMethod.CRYPTOGRAM_3DS
                    )
                )
            )
        ),
        transactionInfo = TransactionInfo(
            currencyCode = "USD",
            totalPriceStatus = TotalPriceStatus.FINAL,
            totalPrice = "99.99"
        )
    )
}
```

| Property  | Description  |
|  --- | --- |
| `paymentDataRequest`PaymentDataRequest | The Google Pay payment request configuration containing payment methods and transaction details. |
| `paymentDataRequest.allowedPaymentMethods`List<PaymentMethodSpecification> | List of supported payment methods. At least one payment method is required. |
| `paymentDataRequest.allowedPaymentMethods[].parameters`PaymentMethodParameters | Payment method parameters including card networks and authentication methods. |
| `paymentDataRequest.allowedPaymentMethods[].parameters.allowedCardNetworks`List<CardNetwork> | Supported card networks.Possible values:`VISA``MASTERCARD``AMEX``DISCOVER``JCB``INTERAC` |
| `paymentDataRequest.allowedPaymentMethods[].parameters.allowedAuthMethods`List<CardAuthMethod> | Supported authentication methods.Possible values:`PAN_ONLY`: Unencrypted card details`CRYPTOGRAM_3DS`: 3DS cryptogram authentication |
| `paymentDataRequest.transactionInfo`TransactionInfo | Details about the transaction, including the amount and currency. |
| `paymentDataRequest.transactionInfo.currencyCode`String | The currency code, in ISO 4217 format (e.g., `USD`, `GBP`, `EUR`). |
| `paymentDataRequest.transactionInfo.totalPriceStatus`TotalPriceStatus | The price finality status.Possible values:`FINAL`: Price is final`ESTIMATED`: Price is estimated |
| `paymentDataRequest.transactionInfo.totalPrice`String | The total monetary value of the transaction as a string (e.g., `99.99`). |


### Advanced configuration

For more complex implementations, you can configure additional settings and features:

```kotlin
import com.pxp.checkout.components.googlepay.types.*
import com.pxp.checkout.services.kount.*

val config = GooglePayButtonComponentConfig().apply {
    // Payment details
    paymentDataRequest = PaymentDataRequest(
        allowedPaymentMethods = listOf(
            PaymentMethodSpecification(
                parameters = PaymentMethodParameters(
                    allowedCardNetworks = listOf(
                        CardNetwork.VISA,
                        CardNetwork.MASTERCARD,
                        CardNetwork.AMEX
                    ),
                    allowedAuthMethods = listOf(
                        CardAuthMethod.PAN_ONLY,
                        CardAuthMethod.CRYPTOGRAM_3DS
                    ),
                    billingAddressRequired = true,
                    billingAddressParameters = BillingAddressParameters(
                        format = BillingAddressFormat.FULL,
                        phoneNumberRequired = true
                    )
                )
            )
        ),
        transactionInfo = TransactionInfo(
            currencyCode = "USD",
            totalPriceStatus = TotalPriceStatus.FINAL,
            totalPrice = "99.99",
            countryCode = "US"
        ),
        emailRequired = true,
        shippingAddressRequired = false
    )
    
    // Button appearance
    style = GooglePayButtonStyle(
        type = GooglePayButtonType.BUY,
        theme = GooglePayButtonTheme.DARK,
        cornerRadius = 8
    )
    
    // Enable 3DS authentication
    useUnityAuthenticationStrategy = true
    
    // CVC collection
    collectCvc = GooglePayButtonComponentConfig.CvcCollectionStrategy.DEFAULT
    
    // Event callbacks
    onPreAuthorisation = suspend { data ->
        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"
                        )
                    )
                )
            )
        )
    }
    
    onPostAuthorisation = { result, paymentData ->
        when (result) {
            is MerchantSubmitResult -> handleSuccess(result)
            is FailedSubmitResult -> handleFailure(result)
        }
    }
    
    onError = { error ->
        Log.e("GooglePay", "Error: ${error.message}")
    }
}
```

## Styling

### Button types and themes

The Google Pay button can be customised to match your app's design:

```kotlin
style = GooglePayButtonStyle(
    type = GooglePayButtonType.CHECKOUT,  // Button text
    theme = GooglePayButtonTheme.DARK,    // Color theme
    cornerRadius = 12                     // Corner radius in dp
)
```

#### Button types

```kotlin
enum class GooglePayButtonType {
    BUY,        // "Buy with G Pay"
    PAY,        // "Pay with G Pay" (default)
    CHECKOUT,   // "Checkout with G Pay"
    BOOK,       // "Book with G Pay"
    DONATE,     // "Donate with G Pay"
    ORDER,      // "Order with G Pay"
    SUBSCRIBE,  // "Subscribe with G Pay"
    PLAIN       // Google Pay logo only
}
```

#### Themes

```kotlin
enum class GooglePayButtonTheme {
    DARK,   // Black background with white text
    LIGHT   // White background with black text
}
```

### Button sizing

Control button size using standard Compose modifiers:

```kotlin
googlePayComponent?.Content(
    modifier = Modifier
        .fillMaxWidth()    // Full width
        .height(48.dp)     // Fixed height
)
```

## Event callbacks

Configure callbacks to handle payment events. See [Events](/guides/checkout/components/android/google-pay/events) for detailed documentation.

### Essential callbacks

```kotlin
val config = GooglePayButtonComponentConfig().apply {
    // Before payment authorisation
    onPreAuthorisation = suspend { data ->
        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"
                        )
                    )
                )
            )
        )
    }
    
    // After payment authorisation
    onPostAuthorisation = { result, paymentData ->
        when (result) {
            is MerchantSubmitResult -> {
                Log.d("GooglePay", "Success: ${result.merchantTransactionId}")
                navigateToSuccess()
            }
            is FailedSubmitResult -> {
                Log.e("GooglePay", "Failed: ${result.errorReason}")
                showError(result.errorReason)
            }
        }
    }
    
    // Error handling
    onError = { error ->
        Log.e("GooglePay", "Error: ${error.message}", error)
        showError(error.message)
    }
    
    // User cancellation
    onCancel = {
        Log.d("GooglePay", "Payment cancelled")
    }
}
```

## Payment sheet interactions

### Overview

Google Pay's payment sheet supports dynamic interactions that allow you to provide real-time updates based on user selections. This creates a seamless, interactive checkout experience where shipping costs, taxes, and totals update automatically as customers make choices within the payment sheet.

Payment sheet interactions enable you to:

* **Calculate shipping costs dynamically** based on the customer's selected address
* **Update transaction amounts** in real-time without reloading the payment sheet
* **Validate addresses** and show errors for unserviceable locations
* **Offer multiple shipping options** with different costs and delivery times
* **Apply discounts or offers** based on customer selections
* **Collect email addresses** for order confirmations
* **Request billing addresses** for payment verification


Payment sheet interactions provide a native app-like experience whilst keeping customers within the Google Pay flow, significantly reducing checkout friction and cart abandonment.

### Enabling payment sheet interactions

To enable dynamic interactions, configure your payment request with the required parameters:

```kotlin
import com.pxp.checkout.components.googlepay.types.*

val config = GooglePayButtonComponentConfig().apply {
    paymentDataRequest = PaymentDataRequest(
        allowedPaymentMethods = listOf(
            PaymentMethodSpecification(
                parameters = PaymentMethodParameters(
                    allowedCardNetworks = listOf(
                        CardNetwork.VISA,
                        CardNetwork.MASTERCARD,
                        CardNetwork.AMEX
                    ),
                    allowedAuthMethods = listOf(
                        CardAuthMethod.PAN_ONLY,
                        CardAuthMethod.CRYPTOGRAM_3DS
                    )
                )
            )
        ),
        transactionInfo = TransactionInfo(
            currencyCode = "GBP",
            totalPriceStatus = TotalPriceStatus.FINAL,
            totalPrice = "50.00",
            displayItems = listOf(
                DisplayItem(
                    label = "Subtotal",
                    type = DisplayItemType.LINE_ITEM,
                    price = "45.00"
                ),
                DisplayItem(
                    label = "Estimated Tax",
                    type = DisplayItemType.TAX,
                    price = "5.00"
                )
            )
        ),
        
        // Enable shipping address collection
        shippingAddressRequired = true,
        shippingAddressParameters = ShippingAddressParameters(
            allowedCountryCodes = listOf("GB", "FR", "DE", "US"),
            phoneNumberRequired = true
        )
    )
    
    // Handle payment data changes
    onPaymentDataChanged = { intermediatePaymentData ->
        handlePaymentDataChange(intermediatePaymentData)
    }
}
```

### Shipping address interactions

#### Collecting shipping addresses

Request shipping addresses by configuring the payment request:

```kotlin
val config = GooglePayButtonComponentConfig().apply {
    paymentDataRequest = PaymentDataRequest(
        allowedPaymentMethods = listOf(/* ... */),
        transactionInfo = TransactionInfo(/* ... */),
        
        // Enable shipping address collection
        shippingAddressRequired = true,
        
        // Configure shipping address parameters
        shippingAddressParameters = ShippingAddressParameters(
            allowedCountryCodes = listOf("GB", "FR", "DE", "US"), // Restrict to specific countries
            phoneNumberRequired = true // Request phone number
        )
    )
    
    onPaymentDataChanged = { intermediatePaymentData ->
        intermediatePaymentData.shippingAddress?.let { address ->
            // Calculate shipping and update totals
            handleShippingAddressChange(address)
        } ?: PaymentDataChangedResponse()
    }
}
```

#### Handling address changes

Respond to address changes with updated pricing and shipping options:

```kotlin
suspend fun handleShippingAddressChange(address: Address): PaymentDataChangedResponse {
    Log.d("GooglePay", "Shipping address changed: ${address.countryCode}, ${address.postalCode}")
    
    return try {
        // 1. Validate the address
        if (!validateShippingAddress(address)) {
            return PaymentDataChangedResponse(
                error = PaymentDataError(
                    reason = PaymentDataErrorReason.SHIPPING_ADDRESS_INVALID,
                    message = "Please enter a complete address",
                    intent = PaymentDataErrorIntent.SHIPPING_ADDRESS
                )
            )
        }
        
        // 2. Check if we ship to this location
        if (!checkShippingAvailability(address.countryCode)) {
            return PaymentDataChangedResponse(
                error = PaymentDataError(
                    reason = PaymentDataErrorReason.SHIPPING_ADDRESS_UNSERVICEABLE,
                    message = "We do not currently ship to ${address.countryCode}",
                    intent = PaymentDataErrorIntent.SHIPPING_ADDRESS
                )
            )
        }
        
        // 3. Calculate shipping cost
        val shippingCost = calculateShipping(address)
        
        // 4. Calculate tax
        val subtotal = 45.00
        val tax = calculateTax(address, subtotal)
        
        // 5. Calculate new total
        val newTotal = subtotal + tax + shippingCost
        
        // 6. Update SDK amount (synchronise with backend)
        pxpCheckout.updateAmount(newTotal)
        
        // 7. Return updated transaction info
        PaymentDataChangedResponse(
            newTransactionInfo = TransactionInfo(
                totalPriceStatus = TotalPriceStatus.FINAL,
                totalPrice = newTotal.toPrice(),
                totalPriceLabel = "Total",
                displayItems = listOf(
                    DisplayItem(
                        label = "Subtotal",
                        type = DisplayItemType.LINE_ITEM,
                        price = subtotal.toPrice(),
                        status = DisplayItemStatus.FINAL
                    ),
                    DisplayItem(
                        label = "Shipping",
                        type = DisplayItemType.LINE_ITEM,
                        price = shippingCost.toPrice(),
                        status = DisplayItemStatus.FINAL
                    ),
                    DisplayItem(
                        label = "Tax",
                        type = DisplayItemType.TAX,
                        price = tax.toPrice(),
                        status = DisplayItemStatus.FINAL
                    )
                )
            )
        )
    } catch (e: Exception) {
        Log.e("GooglePay", "Error handling address change", e)
        PaymentDataChangedResponse(
            error = PaymentDataError(
                reason = PaymentDataErrorReason.SHIPPING_ADDRESS_INVALID,
                message = "Unable to calculate shipping. Please try again.",
                intent = PaymentDataErrorIntent.SHIPPING_ADDRESS
            )
        )
    }
}
```

#### Address validation examples

```kotlin
fun validateShippingAddress(address: Address): Boolean {
    val requiredFields = listOf(
        address.countryCode,
        address.postalCode,
        address.administrativeArea
    )
    
    if (requiredFields.any { it.isNullOrEmpty() }) {
        Log.e("GooglePay", "Missing required address fields")
        return false
    }
    
    // Validate postal code format for UK
    if (address.countryCode == "GB") {
        val ukPostcodeRegex = Regex("^[A-Z]{1,2}\\d{1,2}[A-Z]?\\s?\\d[A-Z]{2}$", RegexOption.IGNORE_CASE)
        if (!ukPostcodeRegex.matches(address.postalCode ?: "")) {
            Log.e("GooglePay", "Invalid UK postcode format")
            return false
        }
    }
    
    return true
}

suspend fun checkShippingAvailability(countryCode: String): Boolean {
    val shippableCountries = listOf("GB", "US", "CA", "FR", "DE", "ES", "IT")
    return shippableCountries.contains(countryCode)
}

suspend fun calculateShipping(address: Address): Double {
    val shippingRates = mapOf(
        "GB" to 4.99,
        "US" to 9.99,
        "CA" to 12.99,
        "FR" to 8.99,
        "DE" to 8.99,
        "ES" to 8.99,
        "IT" to 8.99
    )
    
    return shippingRates[address.countryCode] ?: 15.99 // Default international rate
}

suspend fun calculateTax(address: Address, subtotal: Double): Double {
    val taxRates = mapOf(
        "GB" to 0.20, // 20% VAT
        "US" to 0.08, // Example sales tax
        "CA" to 0.13, // Example HST
        "FR" to 0.20, // 20% TVA
        "DE" to 0.19  // 19% MwSt
    )
    
    val rate = taxRates[address.countryCode] ?: 0.0
    return subtotal * rate
}

// Helper extension function
fun Double.toPrice(): String = "%.2f".format(this)
```

### Shipping option interactions

#### Configuring shipping options

Provide multiple shipping methods for customers to choose from:

```kotlin
val config = GooglePayButtonComponentConfig().apply {
    paymentDataRequest = PaymentDataRequest(
        allowedPaymentMethods = listOf(/* ... */),
        transactionInfo = TransactionInfo(/* ... */),
        
        // Enable shipping option selection
        shippingOptionRequired = true,
        
        // Configure available shipping options
        shippingOptionParameters = ShippingOptionParameters(
            defaultSelectedOptionId = "standard",
            shippingOptions = listOf(
                ShippingOption(
                    id = "standard",
                    label = "Standard Delivery",
                    description = "5-7 business days"
                ),
                ShippingOption(
                    id = "express",
                    label = "Express Delivery",
                    description = "2-3 business days"
                ),
                ShippingOption(
                    id = "next-day",
                    label = "Next Day Delivery",
                    description = "Order by 6pm for next day"
                )
            )
        )
    )
    
    onPaymentDataChanged = { intermediatePaymentData ->
        intermediatePaymentData.shippingOptionData?.let { shippingOption ->
            handleShippingOptionChange(shippingOption)
        } ?: PaymentDataChangedResponse()
    }
}
```

#### Handling shipping option changes

Update pricing when the customer selects a different shipping method:

```kotlin
fun handleShippingOptionChange(shippingOption: ShippingOptionData): PaymentDataChangedResponse {
    Log.d("GooglePay", "Shipping option changed: ${shippingOption.id}")
    
    // Define shipping costs
    val shippingCosts = mapOf(
        "standard" to 4.99,
        "express" to 9.99,
        "next-day" to 14.99
    )
    
    val subtotal = 45.00
    val tax = 9.00
    val shippingCost = shippingCosts[shippingOption.id] ?: 4.99
    
    // Calculate new total
    val newTotal = subtotal + tax + shippingCost
    
    // Update SDK amount
    pxpCheckout.updateAmount(newTotal)
    
    // Track shipping method selection
    trackEvent("shipping-option-selected", mapOf(
        "optionId" to shippingOption.id,
        "cost" to shippingCost
    ))
    
    return PaymentDataChangedResponse(
        newTransactionInfo = TransactionInfo(
            totalPriceStatus = TotalPriceStatus.FINAL,
            totalPrice = newTotal.toPrice(),
            totalPriceLabel = "Total",
            displayItems = listOf(
                DisplayItem(
                    label = "Subtotal",
                    type = DisplayItemType.LINE_ITEM,
                    price = subtotal.toPrice()
                ),
                DisplayItem(
                    label = shippingOption.label,
                    type = DisplayItemType.LINE_ITEM,
                    price = shippingCost.toPrice()
                ),
                DisplayItem(
                    label = "Tax",
                    type = DisplayItemType.TAX,
                    price = tax.toPrice()
                )
            )
        )
    )
}
```

### Email collection

Request the customer's email address for order confirmations:

```kotlin
val config = GooglePayButtonComponentConfig().apply {
    paymentDataRequest = PaymentDataRequest(
        allowedPaymentMethods = listOf(/* ... */),
        transactionInfo = TransactionInfo(/* ... */),
        
        // Request email address
        emailRequired = true
    )
    
    onPostAuthorisation = { result, paymentData ->
        when (result) {
            is MerchantSubmitResult -> {
                // Access email from payment data
                val customerEmail = paymentData.email
                
                // Send confirmation email
                sendOrderConfirmation(
                    email = customerEmail,
                    orderId = result.merchantTransactionId,
                    systemTransactionId = result.systemTransactionId
                )
            }
            else -> { /* Handle failure */ }
        }
    }
}
```

### Billing address collection

Request billing address for verification:

```kotlin
val config = GooglePayButtonComponentConfig().apply {
    paymentDataRequest = PaymentDataRequest(
        allowedPaymentMethods = listOf(
            PaymentMethodSpecification(
                parameters = PaymentMethodParameters(
                    allowedCardNetworks = listOf(
                        CardNetwork.VISA,
                        CardNetwork.MASTERCARD
                    ),
                    allowedAuthMethods = listOf(
                        CardAuthMethod.PAN_ONLY,
                        CardAuthMethod.CRYPTOGRAM_3DS
                    ),
                    
                    // Request billing address
                    billingAddressRequired = true,
                    billingAddressParameters = BillingAddressParameters(
                        format = BillingAddressFormat.FULL, // or MIN for minimal details
                        phoneNumberRequired = false
                    )
                )
            )
        ),
        transactionInfo = TransactionInfo(/* ... */)
    )
}
```

#### Billing address formats

| Format | Fields included |
|  --- | --- |
| `MIN` | Name, country code, postal code |
| `FULL` | Full billing address with street, city, state, postal code, country |


### Complete interaction example

Here's a comprehensive example with all interaction types:

```kotlin
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
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.types.ComponentType

@Composable
fun GooglePayCheckoutWithInteractions(pxpCheckout: PxpCheckout) {
    val googlePayComponent = remember {
        pxpCheckout.createComponent<
            GooglePayButtonComponent,
            GooglePayButtonComponentConfig
        >(
            type = ComponentType.GOOGLE_PAY_BUTTON,
            config = GooglePayButtonComponentConfig().apply {
                // Payment configuration with all interactions
                paymentDataRequest = PaymentDataRequest(
                    allowedPaymentMethods = listOf(
                        PaymentMethodSpecification(
                            parameters = PaymentMethodParameters(
                                allowedCardNetworks = listOf(
                                    CardNetwork.VISA,
                                    CardNetwork.MASTERCARD,
                                    CardNetwork.AMEX
                                ),
                                allowedAuthMethods = listOf(
                                    CardAuthMethod.PAN_ONLY,
                                    CardAuthMethod.CRYPTOGRAM_3DS
                                ),
                                billingAddressRequired = true,
                                billingAddressParameters = BillingAddressParameters(
                                    format = BillingAddressFormat.FULL,
                                    phoneNumberRequired = true
                                )
                            )
                        )
                    ),
                    transactionInfo = TransactionInfo(
                        currencyCode = "GBP",
                        countryCode = "GB",
                        totalPriceStatus = TotalPriceStatus.FINAL,
                        totalPrice = "45.00",
                        totalPriceLabel = "Total",
                        displayItems = listOf(
                            DisplayItem(
                                label = "Wireless Headphones",
                                type = DisplayItemType.LINE_ITEM,
                                price = "39.99",
                                status = DisplayItemStatus.FINAL
                            ),
                            DisplayItem(
                                label = "Estimated Tax",
                                type = DisplayItemType.TAX,
                                price = "5.01",
                                status = DisplayItemStatus.PENDING
                            )
                        )
                    ),
                    
                    // Email collection
                    emailRequired = true,
                    
                    // Shipping address collection
                    shippingAddressRequired = true,
                    shippingAddressParameters = ShippingAddressParameters(
                        allowedCountryCodes = listOf("GB", "FR", "DE", "ES", "IT", "NL", "BE"),
                        phoneNumberRequired = true
                    ),
                    
                    // Shipping options
                    shippingOptionRequired = true,
                    shippingOptionParameters = ShippingOptionParameters(
                        defaultSelectedOptionId = "standard",
                        shippingOptions = listOf(
                            ShippingOption(
                                id = "standard",
                                label = "Standard Delivery",
                                description = "5-7 business days"
                            ),
                            ShippingOption(
                                id = "express",
                                label = "Express Delivery",
                                description = "2-3 business days"
                            )
                        )
                    )
                )
                
                // Handle dynamic updates
                onPaymentDataChanged = { intermediatePaymentData ->
                    handleDynamicPaymentUpdates(intermediatePaymentData, pxpCheckout)
                }
                
                // Handle successful payment
                onPostAuthorisation = { result, paymentData ->
                    when (result) {
                        is MerchantSubmitResult -> {
                            Log.d("GooglePay", "Payment successful with collected data: " +
                                "email=${paymentData.email}, " +
                                "shippingAddress=${paymentData.shippingAddress}, " +
                                "shippingOption=${paymentData.shippingOption}")
                            
                            // Send confirmation email
                            sendOrderConfirmation(
                                email = paymentData.email,
                                orderId = result.merchantTransactionId,
                                systemTransactionId = result.systemTransactionId,
                                shippingAddress = paymentData.shippingAddress
                            )
                            
                            navigateToOrderConfirmation()
                        }
                        else -> { /* Handle failure */ }
                    }
                }
            }
        )
    }
    
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        Text(
            text = "Complete Your Purchase",
            style = MaterialTheme.typography.headlineMedium,
            modifier = Modifier.padding(bottom = 16.dp)
        )
        
        googlePayComponent?.Content(
            modifier = Modifier
                .fillMaxWidth()
                .height(56.dp)
        )
    }
}

fun handleDynamicPaymentUpdates(
    intermediatePaymentData: IntermediatePaymentData,
    pxpCheckout: PxpCheckout
): PaymentDataChangedResponse {
    Log.d("GooglePay", "Payment data changed: " +
        "hasShippingAddress=${intermediatePaymentData.shippingAddress != null}, " +
        "hasShippingOption=${intermediatePaymentData.shippingOptionData != null}")
    
    return try {
        val subtotal = 39.99
        var shippingCost = 0.0
        var tax = 0.0
        var error: PaymentDataError? = null
        
        // Handle shipping address change
        intermediatePaymentData.shippingAddress?.let { address ->
            // Validate address
            if (address.postalCode.isNullOrEmpty() || address.countryCode.isNullOrEmpty()) {
                error = PaymentDataError(
                    reason = PaymentDataErrorReason.SHIPPING_ADDRESS_INVALID,
                    message = "Please enter a complete address",
                    intent = PaymentDataErrorIntent.SHIPPING_ADDRESS
                )
            }
            // Check if we ship to this country
            else if (address.countryCode !in listOf("GB", "FR", "DE", "ES", "IT", "NL", "BE")) {
                error = PaymentDataError(
                    reason = PaymentDataErrorReason.SHIPPING_ADDRESS_UNSERVICEABLE,
                    message = "We do not ship to ${address.countryCode}",
                    intent = PaymentDataErrorIntent.SHIPPING_ADDRESS
                )
            }
            // Calculate shipping and tax
            else {
                val shippingRates = mapOf(
                    "GB" to 4.99,
                    "FR" to 8.99,
                    "DE" to 8.99,
                    "ES" to 8.99,
                    "IT" to 8.99,
                    "NL" to 8.99,
                    "BE" to 8.99
                )
                shippingCost = shippingRates[address.countryCode] ?: 4.99
                
                // Calculate tax
                val taxRates = mapOf(
                    "GB" to 0.20,
                    "FR" to 0.20,
                    "DE" to 0.19,
                    "ES" to 0.21,
                    "IT" to 0.22,
                    "NL" to 0.21,
                    "BE" to 0.21
                )
                tax = subtotal * (taxRates[address.countryCode] ?: 0.20)
            }
        }
        
        // Handle shipping option change
        intermediatePaymentData.shippingOptionData?.let { option ->
            val shippingCosts = mapOf(
                "standard" to 4.99,
                "express" to 9.99
            )
            shippingCost = shippingCosts[option.id] ?: 4.99
        }
        
        // Calculate new total
        val newTotal = subtotal + tax + shippingCost
        
        // Update SDK amount
        pxpCheckout.updateAmount(newTotal)
        
        // Return response
        PaymentDataChangedResponse(
            newTransactionInfo = TransactionInfo(
                totalPriceStatus = TotalPriceStatus.FINAL,
                totalPrice = newTotal.toPrice(),
                totalPriceLabel = "Total",
                displayItems = listOf(
                    DisplayItem(
                        label = "Wireless Headphones",
                        type = DisplayItemType.LINE_ITEM,
                        price = subtotal.toPrice(),
                        status = DisplayItemStatus.FINAL
                    ),
                    DisplayItem(
                        label = "Shipping",
                        type = DisplayItemType.LINE_ITEM,
                        price = shippingCost.toPrice(),
                        status = DisplayItemStatus.FINAL
                    ),
                    DisplayItem(
                        label = "VAT",
                        type = DisplayItemType.TAX,
                        price = tax.toPrice(),
                        status = DisplayItemStatus.FINAL
                    )
                )
            ),
            error = error
        )
    } catch (e: Exception) {
        Log.e("GooglePay", "Error in onPaymentDataChanged", e)
        PaymentDataChangedResponse(
            error = PaymentDataError(
                reason = PaymentDataErrorReason.OTHER_ERROR,
                message = "An error occurred. Please try again.",
                intent = PaymentDataErrorIntent.SHIPPING_ADDRESS
            )
        )
    }
}
```

## CVC collection

Configure when and how to collect CVC codes:

```kotlin
// CVC collection strategy
collectCvc = GooglePayButtonComponentConfig.CvcCollectionStrategy.DEFAULT

// Custom CVC dialog (optional)
cvcVerificationPopupConfig = CvcVerificationDialogConfig(
    title = "Security Verification",
    description = "Please enter your card's CVV code",
    submitButtonText = "Confirm Payment",
    cancelButtonText = "Go Back"
)
```

These strategies control when the SDK prompts customers to enter their CVV/CVC code:

```kotlin
enum class CvcCollectionStrategy {
    ALWAYS,   // Always show CVC dialog for FPAN
    NEVER,    // Never show CVC dialog
    DEFAULT   // Show CVC only when 3DS not available/skipped
}
```

## 3D Secure authentication

### Authentication strategy

The `useUnityAuthenticationStrategy` property controls how 3DS authentication is handled:

```kotlin
useUnityAuthenticationStrategy: Boolean = false
```

- `true`: Uses Unity's automatic authentication strategy. The SDK automatically handles authentication decisions and session updates based on backend responses. This is the **recommended** approach as it simplifies integration and ensures consistent authentication handling.
- `false` (default): Uses manual authentication callbacks. You must handle authentication decisions and session updates in `onPostInitiateAuthentication` and `onPostAuthentication` callbacks.


When using `useUnityAuthenticationStrategy = true`, you still need to provide the `onPreInitiateAuthentication` and `onPreAuthentication` callbacks to configure authentication parameters, but the SDK will automatically handle the post-authentication logic.

### Example configuration

Enable 3DS authentication with Unity's automatic strategy:

```kotlin
val config = GooglePayButtonComponentConfig().apply {
    // Enable Unity's automatic 3DS handling
    useUnityAuthenticationStrategy = true
    
    // Minimal 3DS configuration
    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
        )
    }
}
```

See [3DS](/guides/checkout/components/android/google-pay/3ds) for detailed 3DS configuration.

## Consent and recurring payments

Enable consent collection for payment tokens:

```kotlin
// 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
    )
)

// Attach to Google Pay config
val googlePayConfig = GooglePayButtonComponentConfig().apply {
    googlePayConsentComponent = consentComponent
    
    // Or use callback
    onGetConsent = {
        consentComponent?.getValue() as? Boolean ?: false
    }
}
```

See [Recurring payments](/guides/checkout/components/android/google-pay/recurring-payments) for more details.

## Component lifecycle

### Creating components

```kotlin
val googlePayComponent = pxpCheckout.createComponent<
    GooglePayButtonComponent,
    GooglePayButtonComponentConfig
>(
    type = ComponentType.GOOGLE_PAY_BUTTON,
    config = googlePayConfig
)
```

### Rendering components

```kotlin
@Composable
fun PaymentScreen(pxpCheckout: PxpCheckout) {
    val googlePayComponent = remember {
        pxpCheckout.createComponent<
            GooglePayButtonComponent,
            GooglePayButtonComponentConfig
        >(
            type = ComponentType.GOOGLE_PAY_BUTTON,
            config = googlePayConfig
        )
    }
    
    // Render the component
    googlePayComponent?.Content(
        modifier = Modifier
            .fillMaxWidth()
            .height(56.dp)
    )
}
```

## Complete example

Here's a complete configuration example:

```kotlin
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
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

@Composable
fun GooglePayCheckout(pxpCheckout: PxpCheckout) {
    val googlePayComponent = remember {
        pxpCheckout.createComponent<
            GooglePayButtonComponent,
            GooglePayButtonComponentConfig
        >(
            type = ComponentType.GOOGLE_PAY_BUTTON,
            config = GooglePayButtonComponentConfig().apply {
                // Payment configuration
                paymentDataRequest = PaymentDataRequest(
                    allowedPaymentMethods = listOf(
                        PaymentMethodSpecification(
                            parameters = PaymentMethodParameters(
                                allowedCardNetworks = listOf(
                                    CardNetwork.VISA,
                                    CardNetwork.MASTERCARD,
                                    CardNetwork.AMEX
                                ),
                                allowedAuthMethods = listOf(
                                    CardAuthMethod.PAN_ONLY,
                                    CardAuthMethod.CRYPTOGRAM_3DS
                                ),
                                billingAddressRequired = true,
                                billingAddressParameters = BillingAddressParameters(
                                    format = BillingAddressFormat.FULL,
                                    phoneNumberRequired = true
                                )
                            )
                        )
                    ),
                    transactionInfo = TransactionInfo(
                        currencyCode = "GBP",
                        totalPriceStatus = TotalPriceStatus.FINAL,
                        totalPrice = "99.99",
                        countryCode = "GB"
                    ),
                    emailRequired = true
                )
                
                // Button styling
                style = GooglePayButtonStyle(
                    type = GooglePayButtonType.PAY,
                    theme = GooglePayButtonTheme.DARK,
                    cornerRadius = 8
                )
                
                // Enable 3DS
                useUnityAuthenticationStrategy = true
                
                // CVC collection
                collectCvc = GooglePayButtonComponentConfig.CvcCollectionStrategy.DEFAULT
                
                // Pre-authorisation
                onPreAuthorisation = suspend { data ->
                    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
                onPostAuthorisation = { result, paymentData ->
                    when (result) {
                        is MerchantSubmitResult -> {
                            // Payment successful
                            navigateToSuccess(result.merchantTransactionId)
                        }
                        is FailedSubmitResult -> {
                            // Payment failed
                            showError(result.errorReason)
                        }
                    }
                }
                
                // Error handling
                onError = { error ->
                    showError(error.message)
                }
                
                // Cancellation
                onCancel = {
                    Log.d("GooglePay", "Payment cancelled")
                }
            }
        )
    }
    
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        Text(
            text = "Complete Your Purchase",
            style = MaterialTheme.typography.headlineMedium,
            modifier = Modifier.padding(bottom = 16.dp)
        )
        
        googlePayComponent?.Content(
            modifier = Modifier
                .fillMaxWidth()
                .height(56.dp)
        )
    }
}
```

## Best practices

### Configuration organisation

Extract configuration into separate functions for better maintainability:

```kotlin
fun createGooglePayConfig(
    amount: String,
    onSuccess: (String) -> Unit,
    onError: (String) -> Unit
): GooglePayButtonComponentConfig {
    return GooglePayButtonComponentConfig().apply {
        paymentDataRequest = createPaymentDataRequest(amount)
        style = createButtonStyle()
        useUnityAuthenticationStrategy = true
        
        onPreAuthorisation = suspend { data ->
            createTransactionInitData()
        }
        
        onPostAuthorisation = { result, _ ->
            handlePaymentResult(result, onSuccess, onError)
        }
    }
}

private fun createPaymentDataRequest(amount: String): PaymentDataRequest {
    return PaymentDataRequest(
        allowedPaymentMethods = listOf(
            PaymentMethodSpecification(
                parameters = PaymentMethodParameters(
                    allowedCardNetworks = listOf(
                        CardNetwork.VISA,
                        CardNetwork.MASTERCARD
                    ),
                    allowedAuthMethods = listOf(
                        CardAuthMethod.PAN_ONLY,
                        CardAuthMethod.CRYPTOGRAM_3DS
                    )
                )
            )
        ),
        transactionInfo = TransactionInfo(
            currencyCode = "GBP",
            totalPriceStatus = TotalPriceStatus.FINAL,
            totalPrice = amount
        )
    )
}
```

### Error handling

Always implement comprehensive error handling:

```kotlin
onError = { error ->
    when (error) {
        is GooglePayNotReadyException -> {
            showError("Google Pay is not available on this device")
            showAlternativePaymentMethods()
        }
        is GooglePayConfigurationValidationFailedException -> {
            Log.e("GooglePay", "Configuration error", error)
            showError("Payment system configuration error")
        }
        else -> {
            Log.e("GooglePay", "Unexpected error", error)
            showError("An error occurred. Please try again.")
        }
    }
}
```