Skip to content

Configuration

Learn about how to configure the Google Pay button component for Web.

Basic usage

Minimal configuration

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

The SDK automatically configures tokenizationSpecification with the correct gateway and merchant ID from your session. You only need to provide allowedPaymentMethods with the card parameters.

const config = {
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS']
      }
    }],
    transactionInfo: {
      currencyCode: 'USD',
      totalPriceStatus: 'FINAL',
      totalPrice: '99.99'
    }
  }
};
Property Description
paymentDataRequest
GooglePayPaymentDataRequest
required
The Google Pay payment request configuration containing payment methods and transaction details.
paymentDataRequest.allowedPaymentMethods
array
required
An array specifying the supported payment methods. At least one payment method is required.
paymentDataRequest.allowedPaymentMethods[].type
string
required
The payment method type. This must be set to CARD.
paymentDataRequest.allowedPaymentMethods[].parameters
object
required
Configuration for the payment method.
paymentDataRequest.allowedPaymentMethods[].parameters.allowedCardNetworks
array of strings
required
The supported card networks.

Possible values:
  • VISA
  • MASTERCARD
  • AMEX
  • DISCOVER
  • JCB
  • INTERAC
paymentDataRequest.allowedPaymentMethods[].parameters.allowedAuthMethods
array of strings
required
The supported authentication methods.

Possible values:
  • PAN_ONLY: Unencrypted card details
  • CRYPTOGRAM_3DS: 3DS cryptogram authentication
paymentDataRequest.transactionInfo
object
required
Details about the transaction, including the amount and currency.
paymentDataRequest.transactionInfo.currencyCode
string
required
The currency code, in ISO 4217 format (e.g., USD, GBP, EUR).
paymentDataRequest.transactionInfo.totalPriceStatus
string
required
The price finality status.

Possible values:
  • FINAL: Price is final
  • ESTIMATED: Price is estimated
paymentDataRequest.transactionInfo.totalPrice
string
required
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:

const config = {
  // Payment details
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD', 'AMEX'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
        billingAddressRequired: true,
        billingAddressParameters: {
          format: 'FULL',
          phoneNumberRequired: true
        }
      },
      tokenizationSpecification: {
        type: 'PAYMENT_GATEWAY',
        parameters: {
          gatewayMerchantId: 'merchant-12345'
        }
      }
    }],
    transactionInfo: {
      currencyCode: 'USD',
      totalPriceStatus: 'FINAL',
      totalPrice: '99.99',
      totalPriceLabel: 'Total',
      displayItems: [{
        label: 'Subtotal',
        type: 'SUBTOTAL',
        price: '89.99'
      }, {
        label: 'Tax',
        type: 'TAX',
        price: '10.00'
      }]
    },
    merchantInfo: {
      merchantName: 'Your store',
      merchantId: 'BCR2DN4TZ6...'
    },
    emailRequired: true,
    shippingAddressRequired: true,
    shippingAddressParameters: {
      allowedCountryCodes: ['US', 'GB'],
      phoneNumberRequired: true
    },
    shippingOptionRequired: true,
    shippingOptionParameters: {
      defaultSelectedOptionId: 'standard',
      shippingOptions: [{
        id: 'standard',
        label: 'Standard shipping (5-7 days)',
        description: 'Standard delivery',
        price: '5.99'
      }, {
        id: 'express',
        label: 'Express shipping (2-3 days)',
        description: 'Faster delivery',
        price: '12.99'
      }]
    }
  },

  // Button appearance
  style: {
    type: 'buy',
    color: 'default',
    height: '48px',
    width: '100%',
    borderRadius: 8,
    borderType: 'default_border',
    sizeMode: 'fill',
    locale: 'en-US'
  },

  // Payment options
  existingPaymentMethodRequired: false,
  collectCvc: 'default',

  // Consent component
  googlePayConsentComponent: consentInstance,
  onGetConsent: () => {
    return document.getElementById('consent-checkbox')?.checked || false;
  },

  // Event handlers
  onPreAuthorisation: async (data) => {
    return {
      riskScreeningData: {
        performRiskScreening: true
      }
    };
  },

  onPostAuthorisation: (result, paymentData) => {
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      window.location.href = '/success';
    }
  },

  onPaymentDataChanged: async (intermediatePaymentData) => {
    return {
      newTransactionInfo: {
        totalPriceStatus: 'FINAL',
        totalPrice: calculateTotal(intermediatePaymentData).toFixed(2)
      }
    };
  },

  onError: (error) => {
    console.error('Google Pay error:', error);
    showError(error.message);
  },

  onCancel: () => {
    console.log('Payment cancelled by user');
  }
};
PropertyDescription
paymentDataRequest
GooglePayPaymentDataRequest
The Google Pay payment request configuration including payment methods, transaction info, merchant info, and shipping options. See Google Pay API reference.
style
GooglePayButtonStyle
Button styling configuration including type, colour, dimensions, and locale. Defaults to standard styling.
existingPaymentMethodRequired
boolean
Whether to require the user to have an existing payment method saved in their Google account. Defaults to false.
collectCvc
string
The CVC collection mode. Defaults to default.

Possible values:
  • always: Always collect CVC
  • never: Never collect CVC
  • default: Collect CVC when not using 3DS authentication
cvcVerificationPopupConfig
object
Configuration for CVC verification popup display. Defaults to {}.
googlePayConsentComponent
GooglePayConsentComponent
Consent component instance for payment token storage. Defaults to null. See Recurring payments.
onGetConsent
() => boolean
Callback to check if user has given consent for token storage. Return true if consented, false otherwise.
onPreAuthorisation
(data) => Promise<GooglePayTransactionInitData | null>
Event handler called before payment authorisation to provide additional transaction data.
onPostAuthorisation
(result, paymentData) => void
Event handler called after payment authorisation completes.
onPaymentDataChanged
google.payments.api.PaymentDataChangedHandler
Event handler for dynamic payment sheet updates. See Payment sheet interactions below for detailed implementation guide.
onGooglePaymentButtonClicked
(event) => Promise<void>
Event handler called when the Google Pay button is clicked.
onCustomValidation
() => Promise<boolean>
Custom validation handler called before opening the payment sheet. Return true to proceed, false to block.
onPreRetrySoftDecline
(result) => boolean | object
Handler for retry logic after soft decline responses. See 3D Secure.
onError
(error) => void
Event handler called when an error occurs during the payment flow.
onCancel
() => void
Event handler called when the user cancels the payment.

You can find Google's official rules around button styling in their Google Pay Brand Guidelines and API reference.

Styling

Default styling

The Google Pay button component renders with these default styles:

style = {
  type: 'buy',
  color: 'default',
  height: '48px',
  width: '100%',
  borderRadius: 4,
  borderType: 'default_border',
  sizeMode: 'fill'
}

Custom styling

You can override the default appearance by providing custom styles:

const config = {
  paymentDataRequest: {
    // ... payment configuration
  },
  style: {
    type: 'checkout',
    color: 'black',
    height: '56px',
    width: '100%',
    borderRadius: 12,
    borderType: 'no_border',
    sizeMode: 'fill',
    locale: 'en-GB'
  }
};
PropertyDescription
style
GooglePayButtonStyle
Styling options for the Google Pay button appearance.
style.type
string
The button type. Defaults to buy.

Possible values:
  • buy: Standard purchase button
  • book: Booking button
  • checkout: Checkout button
  • donate: Donation button
  • order: Order button
  • pay: Payment button
  • subscribe: Subscription button
  • plain: Plain button without text
style.color
string
The colour of the Google Pay button. Defaults to default.

Possible values:
  • default: Adapts to user's system settings (light/dark mode)
  • black: Black background with white text
  • white: White background with black text
style.height
string
The button height (e.g., 48px, 56px). Minimum 40px, maximum 72px. Defaults to 48px.
style.width
string
The button width (e.g., 100%, 240px). Defaults to 100%.
style.borderRadius
number
The border radius in pixels (0-24). Defaults to 4.
style.borderType
string
The border style. Defaults to default_border.

Possible values:
  • default_border: Standard border
  • no_border: No border
style.sizeMode
string
How the button should size itself. Defaults to fill.

Possible values:
  • fill: Button fills the container width
  • static: Button uses fixed width
style.locale
string
The language/region code (e.g., en-US, fr-FR, ja-JP). Auto-detected from browser if not specified.
style.buttonRootNode
HTMLDocument | ShadowRoot
The DOM context for the button (for Shadow DOM use cases). Defaults to document.

Google Pay has minimum and maximum size requirements to ensure button legibility and brand compliance. The minimum button height is 40px, and the maximum is 72px.

Button size recommendations

DeviceMinimum heightRecommended heightMaximum height
Mobile40px48px56px
Tablet44px52px64px
Desktop48px56px72px

Localisation

Google Pay automatically displays button text in the user's preferred language. You can override this by setting the locale property:

style: {
  type: 'buy',
  color: 'default',
  height: '48px',
  locale: 'fr-FR' // Force French language
}

Event handling

The Google Pay button component provides event handlers to manage the complete payment flow:

const config = {
  paymentDataRequest: {
    // ... payment configuration
  },
  onGooglePaymentButtonClicked: async (event) => { },
  onPreAuthorisation: async (data) => { return null; },
  onPostAuthorisation: (result, paymentData) => { },
  onPaymentDataChanged: async (intermediatePaymentData) => { return {}; },
  onCustomValidation: async () => { return true; },
  onError: (error) => { },
  onCancel: () => { },
  onGetConsent: () => { return false; }
};
CallbackDescription
onGooglePaymentButtonClicked: ((event: Event) => Promise<void>)?Event handler for when the Google Pay button is clicked. Called before the payment sheet opens.
onPreAuthorisation: ((data: PreAuthorizationData \| null) => Promise<GooglePayTransactionInitData \| null>)?Event handler called before authorisation to provide additional transaction data like 3DS or risk screening.
onPostAuthorisation: ((submitResult: BaseSubmitResult \| null, paymentData: AuthorisationPaymentData) => void)?Event handler for when the payment authorisation completes. Receives transaction result and payment data.
onPaymentDataChanged: (google.payments.api.PaymentDataChangedHandler)?Event handler for dynamic updates to the payment sheet (shipping, pricing). See Payment sheet interactions below.
onCustomValidation: (() => Promise<boolean>)?Custom validation handler called before opening payment sheet. Return true to proceed.
onError: ((error: BaseSdkException) => void)?Event handler for when an error prevents the payment from proceeding.
onCancel: (() => void)?Event handler for when the user cancels the payment flow.
onGetConsent: (() => boolean)?Event handler to get user consent status for payment token storage. Return true if user consents.
onPreRetrySoftDecline: ((result: BaseSubmitResult) => boolean \| object)?Handler for retry logic after soft decline. Return true to retry or an object with retry settings.

For detailed information about event data structures and usage patterns, see Events.

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 page reloads
  • 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 callback intents in your payment request:

const googlePayConfig = {
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD', 'AMEX'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS']
      },
      tokenizationSpecification: {
        type: 'PAYMENT_GATEWAY',
        parameters: {
          gatewayMerchantId: 'your-merchant-id'
        }
      }
    }],
    transactionInfo: {
      currencyCode: 'GBP',
      totalPriceStatus: 'FINAL',
      totalPrice: '50.00',
      displayItems: [
        {
          label: 'Subtotal',
          type: 'LINE_ITEM',
          price: '45.00'
        },
        {
          label: 'Estimated Tax',
          type: 'TAX',
          price: '5.00'
        }
      ]
    },
    
    // Enable callbacks for dynamic updates
    callbackIntents: ['SHIPPING_ADDRESS', 'SHIPPING_OPTION'],
    
    // Request shipping information
    shippingAddressRequired: true,
    shippingOptionRequired: true
  },
  
  // Handle payment data changes
  onPaymentDataChanged: async (intermediatePaymentData) => {
    // Dynamic update logic here
  }
};

Callback intents

IntentDescriptionWhen to use
SHIPPING_ADDRESSCalled when shipping address changesCalculate shipping costs and taxes based on location
SHIPPING_OPTIONCalled when shipping option changesUpdate totals based on selected shipping method
OFFERCalled when offer/promo code changesApply or validate promotional offers
PAYMENT_AUTHORIZATIONCalled before final authorisationPerform final validation before payment

The PAYMENT_AUTHORIZATION callback intent is automatically added by the SDK when using payment sheet interactions. You only need to specify SHIPPING_ADDRESS, SHIPPING_OPTION, or OFFER as needed.

Shipping address interactions

Collecting shipping addresses

Request shipping addresses by configuring the payment request:

const googlePayConfig = {
  paymentDataRequest: {
    // ... other configuration
    
    // Enable shipping address collection
    shippingAddressRequired: true,
    
    // Configure shipping address parameters
    shippingAddressParameters: {
      allowedCountryCodes: ['GB', 'FR', 'DE', 'US'], // Restrict to specific countries
      phoneNumberRequired: true // Request phone number
    },
    
    // Enable callback for address changes
    callbackIntents: ['SHIPPING_ADDRESS']
  },
  
  onPaymentDataChanged: async (intermediatePaymentData) => {
    const address = intermediatePaymentData.shippingAddress;
    
    if (address) {
      // Calculate shipping and update totals
      return await handleShippingAddressChange(address);
    }
    
    return {};
  }
};

Handling address changes

Respond to address changes with updated pricing and shipping options:

async function handleShippingAddressChange(address) {
  console.log('Shipping address changed:', {
    countryCode: address.countryCode,
    postalCode: address.postalCode,
    administrativeArea: address.administrativeArea
  });
  
  try {
    // 1. Validate the address
    const isValid = await validateShippingAddress(address);
    if (!isValid) {
      return {
        error: {
          reason: 'SHIPPING_ADDRESS_INVALID',
          message: 'Please enter a complete address',
          intent: 'SHIPPING_ADDRESS'
        }
      };
    }
    
    // 2. Check if we ship to this location
    const canShip = await checkShippingAvailability(address.countryCode);
    if (!canShip) {
      return {
        error: {
          reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
          message: `We do not currently ship to ${address.countryCode}`,
          intent: 'SHIPPING_ADDRESS'
        }
      };
    }
    
    // 3. Calculate shipping cost
    const shippingCost = await calculateShipping(address);
    
    // 4. Calculate tax
    const subtotal = 45.00;
    const tax = await calculateTax(address, subtotal);
    
    // 5. Calculate new total
    const newTotal = subtotal + tax + shippingCost;
    
    // 6. Update SDK amount (synchronise with backend)
    pxpSdk.updateAmount(newTotal);
    
    // 7. Return updated transaction info
    return {
      newTransactionInfo: {
        totalPriceStatus: 'FINAL',
        totalPrice: newTotal.toFixed(2),
        totalPriceLabel: 'Total',
        displayItems: [
          {
            label: 'Subtotal',
            type: 'LINE_ITEM',
            price: subtotal.toFixed(2),
            status: 'FINAL'
          },
          {
            label: 'Shipping',
            type: 'LINE_ITEM',
            price: shippingCost.toFixed(2),
            status: 'FINAL'
          },
          {
            label: 'Tax',
            type: 'TAX',
            price: tax.toFixed(2),
            status: 'FINAL'
          }
        ]
      }
    };
    
  } catch (error) {
    console.error('Error handling address change:', error);
    return {
      error: {
        reason: 'SHIPPING_ADDRESS_INVALID',
        message: 'Unable to calculate shipping. Please try again.',
        intent: 'SHIPPING_ADDRESS'
      }
    };
  }
}

Address validation examples

// Validate address completeness
function validateShippingAddress(address) {
  const requiredFields = ['countryCode', 'postalCode', 'administrativeArea'];
  
  for (const field of requiredFields) {
    if (!address[field]) {
      console.error(`Missing required field: ${field}`);
      return false;
    }
  }
  
  // Validate postal code format
  if (address.countryCode === 'GB') {
    const ukPostcodeRegex = /^[A-Z]{1,2}\d{1,2}[A-Z]?\s?\d[A-Z]{2}$/i;
    if (!ukPostcodeRegex.test(address.postalCode)) {
      console.error('Invalid UK postcode format');
      return false;
    }
  }
  
  return true;
}

// Check shipping availability
async function checkShippingAvailability(countryCode) {
  const shippableCountries = ['GB', 'US', 'CA', 'FR', 'DE', 'ES', 'IT'];
  return shippableCountries.includes(countryCode);
}

// Calculate shipping based on location
async function calculateShipping(address) {
  const shippingRates = {
    'GB': 4.99,
    'US': 9.99,
    'CA': 12.99,
    'FR': 8.99,
    'DE': 8.99,
    'ES': 8.99,
    'IT': 8.99
  };
  
  return shippingRates[address.countryCode] || 15.99; // Default international rate
}

// Calculate tax based on location
async function calculateTax(address, subtotal) {
  const taxRates = {
    'GB': 0.20, // 20% VAT
    'US': 0.08, // Example sales tax
    'CA': 0.13, // Example HST
    'FR': 0.20, // 20% TVA
    'DE': 0.19  // 19% MwSt
  };
  
  const rate = taxRates[address.countryCode] || 0;
  return subtotal * rate;
}

Shipping option interactions

Configuring shipping options

Provide multiple shipping methods for customers to choose from:

const googlePayConfig = {
  paymentDataRequest: {
    // ... other configuration
    
    // Enable shipping option selection
    shippingOptionRequired: true,
    
    // Configure available shipping options
    shippingOptionParameters: {
      defaultSelectedOptionId: 'standard', // Pre-select an option
      shippingOptions: [
        {
          id: 'standard',
          label: 'Standard Delivery',
          description: '5-7 business days'
        },
        {
          id: 'express',
          label: 'Express Delivery',
          description: '2-3 business days'
        },
        {
          id: 'next-day',
          label: 'Next Day Delivery',
          description: 'Order by 6pm for next day'
        }
      ]
    },
    
    // Enable callback for option changes
    callbackIntents: ['SHIPPING_OPTION']
  },
  
  onPaymentDataChanged: async (intermediatePaymentData) => {
    const shippingOption = intermediatePaymentData.shippingOptionData;
    
    if (shippingOption) {
      return await handleShippingOptionChange(shippingOption);
    }
    
    return {};
  }
};

Handling shipping option changes

Update pricing when the customer selects a different shipping method:

async function handleShippingOptionChange(shippingOption) {
  console.log('Shipping option changed:', shippingOption.id);
  
  // Define shipping costs
  const shippingCosts = {
    'standard': 4.99,
    'express': 9.99,
    'next-day': 14.99
  };
  
  const subtotal = 45.00;
  const tax = 9.00;
  const shippingCost = shippingCosts[shippingOption.id] || 4.99;
  
  // Calculate new total
  const newTotal = subtotal + tax + shippingCost;
  
  // Update SDK amount
  pxpSdk.updateAmount(newTotal);
  
  // Track shipping method selection
  trackEvent('shipping-option-selected', {
    optionId: shippingOption.id,
    cost: shippingCost
  });
  
  return {
    newTransactionInfo: {
      totalPriceStatus: 'FINAL',
      totalPrice: newTotal.toFixed(2),
      totalPriceLabel: 'Total',
      displayItems: [
        {
          label: 'Subtotal',
          type: 'LINE_ITEM',
          price: subtotal.toFixed(2)
        },
        {
          label: shippingOption.label,
          type: 'LINE_ITEM',
          price: shippingCost.toFixed(2)
        },
        {
          label: 'Tax',
          type: 'TAX',
          price: tax.toFixed(2)
        }
      ]
    }
  };
}

Email collection

Request the customer's email address for order confirmations:

const googlePayConfig = {
  paymentDataRequest: {
    // ... other configuration
    
    // Request email address
    emailRequired: true
  },
  
  onPostAuthorisation: (result, paymentData) => {
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      // Access email from payment data
      const customerEmail = paymentData.Email;
      
      // Send confirmation email
      sendOrderConfirmation(customerEmail, {
        orderId: result.merchantTransactionId,
        systemTransactionId: result.systemTransactionId
      });
    }
  }
};

Billing address collection

Request billing address for verification:

const googlePayConfig = {
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
        
        // Request billing address
        billingAddressRequired: true,
        billingAddressParameters: {
          format: 'FULL', // or 'MIN' for minimal details
          phoneNumberRequired: false
        }
      },
      tokenizationSpecification: {
        type: 'PAYMENT_GATEWAY',
        parameters: {
          gatewayMerchantId: 'your-merchant-id'
        }
      }
    }],
    // ... rest of configuration
  }
};

Billing address formats

FormatFields included
MINName, country code, postal code
FULLFull billing address with street, city, state, postal code, country

Complete interaction example

Here's a comprehensive example with all interaction types:

const googlePayButton = pxpSdk.create('google-pay-button', {
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD', 'AMEX'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
        billingAddressRequired: true,
        billingAddressParameters: {
          format: 'FULL',
          phoneNumberRequired: true
        }
      },
      tokenizationSpecification: {
        type: 'PAYMENT_GATEWAY',
        parameters: {
          gatewayMerchantId: 'merchant-12345'
        }
      }
    }],
    transactionInfo: {
      currencyCode: 'GBP',
      countryCode: 'GB',
      totalPriceStatus: 'FINAL',
      totalPrice: '45.00',
      totalPriceLabel: 'Total',
      displayItems: [
        {
          label: 'Wireless Headphones',
          type: 'LINE_ITEM',
          price: '39.99',
          status: 'FINAL'
        },
        {
          label: 'Estimated Tax',
          type: 'TAX',
          price: '5.01',
          status: 'PENDING'
        }
      ]
    },
    
    // Enable all interactions
    callbackIntents: ['SHIPPING_ADDRESS', 'SHIPPING_OPTION'],
    
    // Email collection
    emailRequired: true,
    
    // Shipping address collection
    shippingAddressRequired: true,
    shippingAddressParameters: {
      allowedCountryCodes: ['GB', 'FR', 'DE', 'ES', 'IT', 'NL', 'BE'],
      phoneNumberRequired: true
    },
    
    // Shipping options
    shippingOptionRequired: true,
    shippingOptionParameters: {
      defaultSelectedOptionId: 'standard',
      shippingOptions: [
        {
          id: 'standard',
          label: 'Standard Delivery',
          description: '5-7 business days'
        },
        {
          id: 'express',
          label: 'Express Delivery',
          description: '2-3 business days'
        }
      ]
    }
  },
  
  // Handle dynamic updates
  onPaymentDataChanged: async (intermediatePaymentData) => {
    console.log('Payment data changed:', {
      callbackTrigger: intermediatePaymentData.callbackTrigger,
      hasShippingAddress: !!intermediatePaymentData.shippingAddress,
      hasShippingOption: !!intermediatePaymentData.shippingOptionData
    });
    
    try {
      const subtotal = 39.99;
      let shippingCost = 0;
      let tax = 0;
      let error = null;
      
      // Handle shipping address change
      if (intermediatePaymentData.shippingAddress) {
        const address = intermediatePaymentData.shippingAddress;
        
        // Validate address
        if (!address.postalCode || !address.countryCode) {
          error = {
            reason: 'SHIPPING_ADDRESS_INVALID',
            message: 'Please enter a complete address',
            intent: 'SHIPPING_ADDRESS'
          };
        }
        // Check if we ship to this country
        else if (!['GB', 'FR', 'DE', 'ES', 'IT', 'NL', 'BE'].includes(address.countryCode)) {
          error = {
            reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
            message: `We do not ship to ${address.countryCode}`,
            intent: 'SHIPPING_ADDRESS'
          };
        }
        // Calculate shipping and tax
        else {
          const shippingRates = {
            'GB': 4.99,
            'FR': 8.99,
            'DE': 8.99,
            'ES': 8.99,
            'IT': 8.99,
            'NL': 8.99,
            'BE': 8.99
          };
          shippingCost = shippingRates[address.countryCode] || 4.99;
          
          // Calculate tax
          const taxRates = {
            'GB': 0.20,
            'FR': 0.20,
            'DE': 0.19,
            'ES': 0.21,
            'IT': 0.22,
            'NL': 0.21,
            'BE': 0.21
          };
          tax = subtotal * (taxRates[address.countryCode] || 0.20);
        }
      }
      
      // Handle shipping option change
      if (intermediatePaymentData.shippingOptionData) {
        const optionId = intermediatePaymentData.shippingOptionData.id;
        const shippingCosts = {
          'standard': 4.99,
          'express': 9.99
        };
        shippingCost = shippingCosts[optionId] || 4.99;
      }
      
      // Calculate new total
      const newTotal = subtotal + tax + shippingCost;
      
      // Update SDK amount
      pxpSdk.updateAmount(newTotal);
      
      // Return updated payment data
      const response = {
        newTransactionInfo: {
          totalPriceStatus: 'FINAL',
          totalPrice: newTotal.toFixed(2),
          totalPriceLabel: 'Total',
          displayItems: [
            {
              label: 'Wireless Headphones',
              type: 'LINE_ITEM',
              price: subtotal.toFixed(2),
              status: 'FINAL'
            },
            {
              label: 'Shipping',
              type: 'LINE_ITEM',
              price: shippingCost.toFixed(2),
              status: 'FINAL'
            },
            {
              label: 'VAT',
              type: 'TAX',
              price: tax.toFixed(2),
              status: 'FINAL'
            }
          ]
        }
      };
      
      // Add error if present
      if (error) {
        response.error = error;
      }
      
      return response;
      
    } catch (error) {
      console.error('Error in onPaymentDataChanged:', error);
      return {
        error: {
          reason: 'OTHER_ERROR',
          message: 'An error occurred. Please try again.',
          intent: 'SHIPPING_ADDRESS'
        }
      };
    }
  },
  
  // Handle successful payment
  onPostAuthorisation: (result, paymentData) => {
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      console.log('Payment successful with collected data:', {
        email: paymentData.Email,
        shippingAddress: paymentData.ShippingAddress,
        shippingOption: paymentData.ShippingOption
      });
      
      // Send confirmation email
      sendOrderConfirmation(paymentData.Email, {
        orderId: result.merchantTransactionId,
        systemTransactionId: result.systemTransactionId,
        shippingAddress: paymentData.ShippingAddress
      });
      
      window.location.href = '/order-confirmation';
    }
  }
});

googlePayButton.mount('google-pay-container');

Methods

The Google Pay button component provides methods for lifecycle management.

mount()

Renders the Google Pay button in the specified container:

const googlePayButton = pxpSdk.create('google-pay-button', config);
googlePayButton.mount('google-pay-container');

unmount()

Removes the Google Pay button from the DOM:

googlePayButton.unmount();

For complete lifecycle management details, see Implementation.

Examples

Basic checkout button

A straightforward implementation with essential configuration and error handling:

const googlePayButton = pxpSdk.create('google-pay-button', {
  // Payment configuration
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD', 'AMEX'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS']
      },
      tokenizationSpecification: {
        type: 'PAYMENT_GATEWAY',
        parameters: {
          gatewayMerchantId: 'merchant-12345'
        }
      }
    }],
    transactionInfo: {
      currencyCode: 'USD',
      totalPriceStatus: 'FINAL',
      totalPrice: '29.99',
      totalPriceLabel: 'Total'
    }
  },

  // Essential event handlers
  onGooglePaymentButtonClicked: async (event) => {
    console.log('Google Pay button clicked');
    trackEvent('google_pay_clicked');
  },

  onPostAuthorisation: (result, paymentData) => {
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      console.log('Payment successful:', result.merchantTransactionId);
      window.location.href = '/order-confirmation';
    } else if (result && 'errorCode' in result) {
      console.error('Payment failed:', result.errorReason);
      showError('Payment failed. Please try again.');
    }
  },

  onError: (error) => {
    console.error('Google Pay error:', error);
    showError('Payment error. Please try again.');
  },

  onCancel: () => {
    console.log('Payment cancelled');
    showMessage('Payment was cancelled.');
  }
});

googlePayButton.mount('google-pay-container');

Custom styled button

Implementation with custom styling for brand consistency:

const googlePayButton = pxpSdk.create('google-pay-button', {
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS']
      },
      tokenizationSpecification: {
        type: 'PAYMENT_GATEWAY',
        parameters: {
          gatewayMerchantId: 'merchant-12345'
        }
      }
    }],
    transactionInfo: {
      currencyCode: 'GBP',
      totalPriceStatus: 'FINAL',
      totalPrice: '49.99'
    }
  },

  // Custom styling
  style: {
    type: 'checkout',
    color: 'black',
    height: '56px',
    width: '100%',
    borderRadius: 12,
    borderType: 'no_border',
    sizeMode: 'fill',
    locale: 'en-GB'
  },

  onPostAuthorisation: (result, paymentData) => {
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      navigateToSuccess(result.merchantTransactionId);
    } else if (result && 'errorCode' in result) {
      handlePaymentError(result.errorReason);
    }
  }
});

googlePayButton.mount('google-pay-container');

Enterprise payment with full features

A comprehensive implementation with dynamic pricing, shipping, and complete event handling:

For detailed implementation of shipping address handling and dynamic pricing logic, see the Payment sheet interactions section above.

const googlePayButton = pxpSdk.create('google-pay-button', {
  // Payment details with shipping
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD', 'AMEX'],
        allowedAuthMethods: ['CRYPTOGRAM_3DS'],
        billingAddressRequired: true,
        billingAddressParameters: {
          format: 'FULL',
          phoneNumberRequired: true
        }
      },
      tokenizationSpecification: {
        type: 'PAYMENT_GATEWAY',
        parameters: {
          gatewayMerchantId: 'enterprise-merchant-789'
        }
      }
    }],
    transactionInfo: {
      currencyCode: 'USD',
      totalPriceStatus: 'FINAL',
      totalPrice: '129.99',
      displayItems: [{
        label: 'Subtotal',
        type: 'SUBTOTAL',
        price: '119.99'
      }, {
        label: 'Shipping',
        type: 'LINE_ITEM',
        price: '10.00'
      }]
    },
    merchantInfo: {
      merchantName: 'Enterprise Store',
      merchantId: 'BCR2DN4TZ6...'
    },
    emailRequired: true,
    shippingAddressRequired: true,
    shippingAddressParameters: {
      allowedCountryCodes: ['US', 'GB', 'CA'],
      phoneNumberRequired: true
    },
    shippingOptionRequired: true,
    shippingOptionParameters: {
      defaultSelectedOptionId: 'standard',
      shippingOptions: [{
        id: 'standard',
        label: 'Standard Shipping (5-7 days)',
        description: 'Standard delivery',
        price: '5.99'
      }, {
        id: 'express',
        label: 'Express Shipping (2-3 days)',
        description: 'Faster delivery',
        price: '12.99'
      }]
    }
  },

  // Custom styling
  style: {
    type: 'buy',
    color: 'default',
    height: '52px',
    width: '100%',
    borderRadius: 8,
    sizeMode: 'fill'
  },

  // Pre-authorisation with transaction data
  onPreAuthorisation: async (data) => {
    console.log('Preparing transaction...');
    
    return {
      riskScreeningData: {
        performRiskScreening: true,
        excludeDeviceData: false,
        items: [{
          price: 119.99,
          quantity: 1,
          category: 'electronics',
          sku: 'PROD-001'
        }]
      },
      psd2Data: {
        scaExemption: 'LowValue'
      }
    };
  },

  // Dynamic payment sheet updates
  onPaymentDataChanged: async (intermediatePaymentData) => {
    const intent = intermediatePaymentData.callbackTrigger;
    
    if (intent === 'SHIPPING_OPTION') {
      const shippingOptionId = intermediatePaymentData.shippingOptionData?.id;
      const shippingCost = shippingOptionId === 'express' ? 12.99 : 5.99;
      const subtotal = 119.99;
      const total = (subtotal + shippingCost).toFixed(2);
      
      return {
        newTransactionInfo: {
          totalPriceStatus: 'FINAL',
          totalPrice: total,
          displayItems: [{
            label: 'Subtotal',
            type: 'SUBTOTAL',
            price: subtotal.toFixed(2)
          }, {
            label: 'Shipping',
            type: 'LINE_ITEM',
            price: shippingCost.toFixed(2)
          }]
        }
      };
    }
    
    if (intent === 'SHIPPING_ADDRESS') {
      const country = intermediatePaymentData.shippingAddress?.countryCode;
      
      // Validate shipping country
      if (!['US', 'GB', 'CA'].includes(country)) {
        return {
          error: {
            reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
            message: 'We do not ship to this country',
            intent: 'SHIPPING_ADDRESS'
          }
        };
      }
    }
    
    return {};
  },

  // Complete event handling
  onGooglePaymentButtonClicked: async (event) => {
    console.log('Google Pay button clicked');
    trackAnalyticsEvent('google_pay_clicked', {
      amount: 129.99,
      currency: 'USD'
    });
  },

  onPostAuthorisation: (result, paymentData) => {
    console.log('Payment completed:', result);
    
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      const orderId = result.merchantTransactionId;
      const email = paymentData.Email;
      const shippingAddress = paymentData.ShippingAddress;
      
      trackAnalyticsEvent('purchase_success', {
        orderId,
        amount: 129.99,
        currency: 'USD'
      });
      
      // Create order with shipping details
      createOrder({
        orderId,
        email,
        shippingAddress,
        amount: 129.99
      });
      
      showSuccessMessage('Payment successful!');
      setTimeout(() => {
        window.location.href = `/order-confirmation?id=${orderId}`;
      }, 2000);
    } else {
      console.error('Payment failed:', result.errorReason);
      trackAnalyticsEvent('purchase_failed', {
        reason: result.errorReason
      });
      showErrorDialog('Payment failed: ' + result.errorReason);
    }
  },

  onError: (error) => {
    const errorMessage = error.ErrorCode === 'SDK_ERROR'
      ? 'Failed to load Google Pay. Please check your connection.'
      : `Payment error: ${error.message}`;
    
    console.error('Google Pay error:', error);
    trackAnalyticsEvent('google_pay_error', {
      errorCode: error.ErrorCode,
      errorMessage: error.message
    });
    
    showErrorDialog(errorMessage);
  },

  onCancel: () => {
    console.log('Payment cancelled');
    trackAnalyticsEvent('google_pay_cancelled');
    showMessage('Payment was cancelled.');
  }
});

googlePayButton.mount('google-pay-container');

Implementation with consent component for saving payment methods:

// Create consent component first
const consentConfig = {
  label: 'Save my payment method for future purchases',
  checked: false
};
const consentComponent = pxpSdk.create('google-pay-consent', consentConfig);
consentComponent.mount('consent-container');

// Create Google Pay button with linked consent
const googlePayButton = pxpSdk.create('google-pay-button', {
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD'],
        allowedAuthMethods: ['CRYPTOGRAM_3DS']
      },
      tokenizationSpecification: {
        type: 'PAYMENT_GATEWAY',
        parameters: {
          gatewayMerchantId: 'merchant-12345'
        }
      }
    }],
    transactionInfo: {
      currencyCode: 'USD',
      totalPriceStatus: 'FINAL',
      totalPrice: '9.99',
      totalPriceLabel: 'Monthly Subscription'
    }
  },

  // Link consent component
  googlePayConsentComponent: consentComponent,
  
  // Alternative: Use onGetConsent callback
  onGetConsent: () => {
    const hasConsent = consentComponent.getValue() as boolean;
    console.log('User consent:', hasConsent);
    return hasConsent;
  },

  onPostAuthorisation: (result, paymentData) => {
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      const hasConsent = consentComponent.getValue() as boolean;
      
      if (hasConsent) {
        console.log('Payment method saved for recurring payments');
        savePaymentToken(result.merchantTransactionId);
      }
      
      window.location.href = '/subscription-active';
    }
  },

  onError: (error) => {
    console.error('Subscription error:', error);
    showError('Unable to start subscription. Please try again.');
  }
});

googlePayButton.mount('google-pay-container');

The google-pay-consent component is an optional checkbox for collecting customer consent to store payment methods. It can be linked to the Google Pay button for use cases like recurring payments, subscriptions, or express checkout.

The consent component is separate from recurring payment configuration. Recurring payments are enabled by adding recurring configuration to transactionData in SDK initialization. See Recurring payments for details.

Basic usage

const consentComponent = pxpSdk.create('google-pay-consent', {
  label: 'Save my payment method for future purchases',
  checked: false
});

consentComponent.mount('consent-container');

Advanced configuration

const consentComponent = pxpSdk.create('google-pay-consent', {
  // Basic configuration
  label: 'I agree to save my payment method for faster checkout',
  checked: false,
  
  // Visual configuration
  checkedColor: '#4CAF50',
  tabIndex: 5,
  
  // Label styling
  labelStyles: {
    checked: {
      color: '#4CAF50',
      fontWeight: '600',
      fontSize: '14px'
    },
    unchecked: {
      color: '#666666',
      fontSize: '14px',
      fontWeight: '400'
    }
  }
});
PropertyDescription
label
string
Text label displayed next to the checkbox. Defaults to 'I agree to store my card information for faster checkout in the future'.
checked
boolean
Initial checked state of the checkbox. Defaults to false.
checkedColor
string
Custom colour for the checkbox when checked (e.g., '#4CAF50'). Defaults to '#292CF5'.
tabIndex
number
Tab index for keyboard navigation. Defaults to null.
labelStyles
object
Custom styles for the label in different states. Defaults to null.
labelStyles.checked
CSSProperties
Styles applied to the label when checkbox is checked. Defaults to null.
labelStyles.unchecked
CSSProperties
Styles applied to the label when checkbox is unchecked. Defaults to null.

Link the consent component to your Google Pay button:

// Create consent component
const consentComponent = pxpSdk.create('google-pay-consent', {
  label: 'Save my payment method',
  checked: false
});

// Create Google Pay button with consent reference
const googlePayButton = pxpSdk.create('google-pay-button', {
  paymentDataRequest: {
    // ... payment configuration
  },
  
  // Link the consent component
  googlePayConsentComponent: consentComponent,

  onPostAuthorisation: async (result, paymentData) => {
    if (result && 'merchantTransactionId' in result) {
      console.log('Payment successful');
      // Store payment token if consent was given
    }
  }
});

// Mount both components
consentComponent.mount('consent-container');
googlePayButton.mount('google-pay-container');

Using callback instead of component

Alternatively, use the onGetConsent callback if managing consent state separately:

const googlePayButton = pxpSdk.create('google-pay-button', {
  paymentDataRequest: {
    // ... payment configuration
  },
  
  // Simple boolean consent callback
  onGetConsent: () => {
    // Return true if customer has given consent
    return document.getElementById('my-consent-checkbox').checked;
  },
  
  onPostAuthorisation: async (result, paymentData) => {
    // Handle payment based on consent
  }
});

When both googlePayConsentComponent and onGetConsent are provided, the consent component takes priority. The onGetConsent callback will not be called if a consent component is linked.

Customize the appearance for different states:

const consentComponent = pxpSdk.create('google-pay-consent', {
  label: 'Save my payment method',
  checkedColor: '#FF5722',  // Custom brand color
  labelStyles: {
    checked: {
      color: '#2E7D32',
      fontWeight: 'bold',
      fontSize: '16px',
      textDecoration: 'underline'
    },
    unchecked: {
      color: '#757575',
      fontWeight: 'normal',
      fontSize: '14px'
    }
  }
});

Configuration interfaces

GooglePayButtonComponentConfig

The main configuration interface for the Google Pay button component:

interface GooglePayButtonComponentConfig extends BaseComponentConfig {
  paymentDataRequest: GooglePayPaymentDataRequest;
  style?: GooglePayButtonStyle;
  existingPaymentMethodRequired?: boolean;
  collectCvc?: 'always' | 'never' | 'default';
  cvcVerificationPopupConfig?: CvcVerificationPopupConfig;
  googlePayConsentComponent?: GooglePayConsentComponent;
  onGetConsent?: () => boolean;
  onPreAuthorisation?: (data: PreAuthorizationData | null) => Promise<GooglePayTransactionInitData | null>;
  onPostAuthorisation?: (submitResult: BaseSubmitResult | null, paymentData: AuthorisationPaymentData) => void;
  onPaymentDataChanged?: google.payments.api.PaymentDataChangedHandler;
  onGooglePaymentButtonClicked?: (event: Event) => Promise<void>;
  onCustomValidation?: () => Promise<boolean>;
  onPreRetrySoftDecline?: (result: BaseSubmitResult) => boolean | {
    retry: boolean;
    updatedConfigs?: object;
  };
  onError?: (error: BaseSdkException) => void;
  onCancel?: () => void;
}

GooglePayConsentComponentConfig

Configuration interface for the consent component:

interface GooglePayConsentComponentConfig extends BaseComponentConfig {
  label?: string;
  checked?: boolean;
  checkedColor?: string;
  tabIndex?: number;
  labelStyles?: {
    checked?: CSSProperties;
    unchecked?: CSSProperties;
  };
}

GooglePayButtonStyle

Styling configuration interface:

interface GooglePayButtonStyle {
  type?: 'buy' | 'book' | 'checkout' | 'donate' | 'order' | 'pay' | 'subscribe' | 'plain';
  color?: 'default' | 'black' | 'white';
  height?: string;
  width?: string;
  borderRadius?: number;
  borderType?: 'default_border' | 'no_border';
  sizeMode?: 'fill' | 'static';
  buttonRootNode?: HTMLDocument | ShadowRoot | undefined;
  locale?: string;
}