Skip to content

Implementation

Learn how to use the Google Pay component for Web in your project.

Overview

Every component follows the same basic three-step lifecycle:

  1. Create the component and optionally add your own configuration.
  2. Mount the component. This is what makes the component visible and interactive.
  3. Unmount the component. This is a clean-up step that clears up resources.

Before you start

To use the Google Pay component, you first need to:

  • Install Components for Web.
  • Complete the Google Pay onboarding process in the Unity Portal. See Onboarding for details.
  • Ensure your website is served over HTTPS (required for Google Pay).
  • Configure your Google Pay merchant account and link it to the Unity Portal.

Browser and device compatibility

Google Pay for Web has specific requirements for optimal functionality.

Supported browsers

  • Chrome 61+ on all platforms
  • Safari 12.1+ on macOS and iOS
  • Firefox 62+ on all platforms
  • Edge 79+ on Windows and macOS

Device requirements

  • Android devices: Android 5.0 (Lollipop) or later
  • iOS devices: iOS 12.1 or later
  • Desktop: Windows 7+, macOS 10.11+, or Linux

Configuration requirements

  • The customer must have a supported payment method saved in their Google account.
  • For biometric authentication, the device must support fingerprint or face recognition.

Step 1: Initialise the SDK

To get started, initialise the Checkout SDK with Google Pay support.

import { PxpCheckout } from '@pxpio/web-components-sdk';

const pxpSdk = PxpCheckout.initialize({
  environment: 'test', // or 'live' for production
  session: {
    sessionId: 'your-session-id',
    hmacKey: 'your-hmac-key',
    encryptionKey: 'your-encryption-key'
  },
  ownerId: 'your-owner-id',
  ownerType: 'MerchantGroup',
  kountDisabled: false, // OPTIONAL: Set to true to disable Kount fraud detection
  transactionData: {
    currency: 'EUR',
    amount: 25.00,
    merchantTransactionId: crypto.randomUUID(),
    merchantTransactionDate: () => new Date().toISOString(),
    entryType: 'Ecom',
    intent: {
      card: 'Authorisation'
    }
  },
  onGetShopper: () => {
    // Return current shopper data dynamically
    return Promise.resolve({
      id: 'shopper-' + Date.now(),
      email: 'customer@example.com',
      firstName: 'John',
      lastName: 'Doe'
    });
  }
});
PropertyDescription
environment
string
required
The environment type.

Possible values:
  • test: For sandbox.
  • live: For production.
session
sessionData
required
Details about the checkout session.
session.sessionId
string
required
The unique session identifier.
session.hmacKey
string
required
The HMAC key for session validation.
session.encryptionKey
string
required
The encryption key for secure data transmission.
ownerId
string
required
The identifier of the owner related to the ownerType.
ownerType
string
required
The type of owner.

Possible values:
  • MerchantGroup
  • Merchant Coming soon
  • Site Coming soon
onGetShopper
function
Callback function to provide shopper data dynamically. Returns a Promise with shopper information including ID, email, name, and contact details.
transactionData
object
required
Details about the transaction.
transactionData.currency
string (3 characters)
required
The currency code associated with the transaction, in ISO 4217 format.
transactionData.amount
number
required
The transaction amount.
transactionData.entryType
string
required
The entry type.

Possible values:
  • Ecom: E-commerce transactions.
  • Moto: Mail order/telephone order transactions.
transactionData.intent
object
required
The transaction intent.

Possible values for card:
  • Authorisation: Authorise a payment for later capture.
  • Purchase: Capture payment immediately.
  • Verification: Verify payment method without charging.
  • EstimatedAuthorisation: Authorise with estimated amount.
transactionData.merchantTransactionId
string
required
A unique identifier for this transaction.
transactionData.merchantTransactionDate
function
required
A function that returns the date and time of the transaction, in ISO 8601 format.

Step 2: Create the component configuration

Next, you're going to create the Google Pay component configuration.

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', 'AMEX'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS']
      }
    }],
    transactionInfo: {
      currencyCode: 'EUR',
      totalPriceStatus: 'FINAL',
      totalPrice: '25.00',
      totalPriceLabel: 'Total',
      displayItems: [
        {
          label: 'Product',
          type: 'LINE_ITEM',
          price: '20.00',
          status: 'FINAL'
        },
        {
          label: 'Tax',
          type: 'TAX',
          price: '5.00',
          status: 'FINAL'
        }
      ]
    }
  },
  style: {
    type: 'buy',
    color: 'default',
    height: '48px',
    borderRadius: 4
  },
  onPreAuthorisation: async (data) => {
    console.log('Pre-authorisation started');
    return {
      // Transaction initialisation data
    };
  },
  onPostAuthorisation: async (result, paymentData) => {
    console.log('Payment completed');
    console.log('Merchant Transaction ID:', result.merchantTransactionId);
    console.log('System Transaction ID:', result.systemTransactionId);
    
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      // Use merchantTransactionId & systemTransactionId to get transaction details from Unity
      window.location.href = '/payment-success';
    }
  },
  onError: (error) => {
    console.error('Google Pay error:', error);
  }
};
ParameterDescription
paymentDataRequest
object
required
Google Pay payment request configuration.
paymentDataRequest.allowedPaymentMethods
array of objects
required
Specifies support for one or more payment methods.
paymentDataRequest.allowedPaymentMethods.type
string
required
The payment method type. Currently only CARD is supported.
paymentDataRequest.allowedPaymentMethods.parameters.allowedCardNetworks
array of strings
required
Supported card networks.

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

Possible values:
  • PAN_ONLY: Card details are provided without cryptogram.
  • CRYPTOGRAM_3DS: Card details are provided with 3DS cryptogram.
paymentDataRequest.allowedPaymentMethods.parameters.billingAddressRequired
boolean
Whether to request billing address. Default is false.
paymentDataRequest.allowedPaymentMethods.parameters.billingAddressParameters
object
Billing address parameters when required.
paymentDataRequest.allowedPaymentMethods.parameters.billingAddressParameters.format
string
Address format.

Possible values:
  • MIN: Name, country code, and postal code.
  • FULL: Full billing address.
paymentDataRequest.allowedPaymentMethods.parameters.billingAddressParameters.phoneNumberRequired
boolean
Whether phone number is required with billing address. Default is false.
paymentDataRequest.transactionInfo
object
Transaction details.
paymentDataRequest.transactionInfo.currencyCode
string (3 characters)
required
The currency code, in ISO 4217 format.
paymentDataRequest.transactionInfo.countryCode
string (2 characters)
The country code, in ISO 3166-1 alpha-2 format.
paymentDataRequest.transactionInfo.totalPriceStatus
string
required
The status of the total price.

Possible values:
  • FINAL: Final amount.
  • ESTIMATED: Estimated amount.
paymentDataRequest.transactionInfo.totalPrice
string
required
The total amount as a string with decimal precision.
paymentDataRequest.transactionInfo.totalPriceLabel
string
A label for the total price.
paymentDataRequest.transactionInfo.displayItems
array of objects
Individual line items for the transaction.
paymentDataRequest.transactionInfo.displayItems.label
string
required
The display name for the line item.
paymentDataRequest.transactionInfo.displayItems.type
string
required
The line item type.

Possible values:
  • LINE_ITEM
  • SUBTOTAL
  • TAX
  • DISCOUNT
  • SHIPPING_OPTION
paymentDataRequest.transactionInfo.displayItems.price
string
required
The line item amount as a string.
paymentDataRequest.transactionInfo.displayItems.status
string
The line item status.

Possible values:
  • FINAL
  • PENDING
paymentDataRequest.emailRequired
boolean
Whether to request email address. Default is false.
paymentDataRequest.shippingAddressRequired
boolean
Whether to request shipping address. Default is false.
paymentDataRequest.shippingAddressParameters
object
Shipping address parameters when required.
paymentDataRequest.shippingAddressParameters.allowedCountryCodes
array of strings
Allowed country codes for shipping.
paymentDataRequest.shippingAddressParameters.phoneNumberRequired
boolean
Whether phone number is required with shipping address.
paymentDataRequest.shippingOptionRequired
boolean
Whether to request shipping option selection. Default is false.
paymentDataRequest.shippingOptionParameters
object
Shipping option parameters when required.
paymentDataRequest.shippingOptionParameters.defaultSelectedOptionId
string
The default selected shipping option ID.
paymentDataRequest.shippingOptionParameters.shippingOptions
array of objects
Available shipping options.
paymentDataRequest.shippingOptionParameters.shippingOptions.id
string
required
Unique identifier for the shipping option.
paymentDataRequest.shippingOptionParameters.shippingOptions.label
string
required
The display name for the shipping option.
paymentDataRequest.shippingOptionParameters.shippingOptions.description
string
Additional details about the shipping option.
paymentDataRequest.callbackIntents
array of strings
Callback intents for dynamic updates.

Possible values:
  • OFFER: Offer updates
  • SHIPPING_ADDRESS: Shipping address changes
  • SHIPPING_OPTION: Shipping option changes


Note: PAYMENT_AUTHORIZATION is automatically added by the SDK and does not need to be specified.
style
object
Button styling configuration.
style.type
string
The button type.

Possible values:
  • buy: Purchase button
  • book: Booking button
  • checkout: Checkout button
  • donate: Donation button
  • order: Order button
  • pay: Payment button
  • subscribe: Subscription button
  • plain: Plain button
style.color
string
The button color.

Possible values:
  • default: Default theme
  • black: Black background
  • white: White background
style.height
string
The button height (e.g., '44px', '48px').
style.width
string
The button width (e.g., '100%', '240px').
style.borderRadius
number
The button border radius in pixels.
style.sizeMode
string
The button size mode.

Possible values:
  • fill: Button fills container width
  • static: Button uses fixed dimensions
existingPaymentMethodRequired
boolean
Whether to require the user to have an existing payment method. Default is false.
collectCvc
string
CVC collection mode.

Possible values:
  • always: Always collect CVC
  • never: Never collect CVC
  • default: Collect CVC when not using 3DS

For a complete configuration reference with all available options, see Configuration.

Step 3: Setup the HTML container

Create an HTML container where the Google Pay button will be mounted.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Google Pay Checkout</title>
</head>
<body>
    <div class="checkout-container">
        <h2>Complete your purchase</h2>
        <div class="payment-summary">
            <div class="item">
                <span>Product</span>
                <span>€20.00</span>
            </div>
            <div class="item">
                <span>Tax</span>
                <span>€5.00</span>
            </div>
            <hr>
            <div class="total">
                <span>Total</span>
                <span>€25.00</span>
            </div>
        </div>
        
        <!-- Google Pay button container -->
        <div id="google-pay-container" style="margin: 20px 0;"></div>
        
        <div class="alternative-payments">
            <p>Or pay with card</p>
            <!-- Other payment methods -->
        </div>
    </div>
</body>
</html>

Step 4: Create and mount the component

For the component to be visible and functional, you need to mount it. Make sure to supply both the component configuration and the container ID.

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

At this point, your Google Pay button is ready to use and will appear in the specified container.

Step 5: Handle payment results

The component will automatically handle the payment flow. You can listen for payment results through the callback functions. For complete event handler reference and callback data structures, see Events.

const config = {
  // ... other configuration
  onPreAuthorisation: async (data) => {
    // Called before payment authorisation
    console.log('Pre-authorisation started');
    return {
      // Optional: Add risk screening data, PSD2 exemptions, etc.
      riskScreeningData: {
        // Kount risk screening data
      },
      psd2Data: {
        scaExemption: 'LowValue' // Optional PSD2 SCA exemption
      }
    };
  },
  onPostAuthorisation: async (result, paymentData) => {
    console.log('Payment completed');
    console.log('Merchant Transaction ID:', result.merchantTransactionId);
    console.log('System Transaction ID:', result.systemTransactionId);
    
    if (result && 'merchantTransactionId' in result) {
      // Success - MerchantSubmitResult
      // Use merchantTransactionId & systemTransactionId to get transaction details from Unity
      console.log('Payment authorised successfully');
      
      // Redirect to success page or show success message
      window.location.href = '/payment-success';
    } else {
      // Payment was declined or error occurred
      console.log('Payment failed:', result.errorReason);
      // Show error message to user
    }
  },
  onError: (error) => {
    console.error('Google Pay error:', error);
    // Handle and display error to user
    // For error handling patterns, see Troubleshooting guide
  },
  onCancel: () => {
    console.log('Payment cancelled by user');
    // Handle cancellation
  }
};

For detailed information about all available event handlers and their callback data, see Events. For error handling best practices, see Troubleshooting.

Step 6: Unmount the component

Lastly, remember to unmount the component when it's no longer needed to free up resources.

googlePayButton.unmount();

What's next?

Add 3D Secure authentication

For enhanced security, integrate 3DS authentication with your Google Pay implementation. See 3D Secure for complete details.

const config = {
  // ... other configuration
  onPreInitiateAuthentication: () => {
    // Configure 3DS pre-initiation
    return {
      providerId: 'your-provider-id',
      requestorAuthenticationIndicator: '01',
      timeout: 30 // Fingerprint timeout in seconds
    };
  },
  onPostInitiateAuthentication: (data) => {
    console.log('3DS pre-initiation complete:', data.authenticationId);
  },
  onPreAuthentication: async () => {
    // Configure full 3DS authentication
    return {
      merchantCountryNumericCode: '276', // Germany
      merchantLegalName: 'Your Store',
      challengeWindowSize: 2,
      requestorChallengeIndicator: '02' // No preference
    };
  },
  onPostAuthentication: (data) => {
    console.log('3DS authentication complete:', data);
  }
};

Learn more about 3DS authentication flows in the 3D Secure and Non-3DS guides.

Enable one-click repeat purchases by storing payment tokens with customer consent. See Recurring payments for complete implementation details.

// Create consent component
const googlePayConsent = pxpSdk.create('google-pay-consent');

// Create button with consent
const googlePayButton = pxpSdk.create('google-pay-button', {
  // ... other configuration
  googlePayConsentComponent: googlePayConsent,
  onGetConsent: () => {
    // Return whether consent was given
    return document.getElementById('consent-checkbox').checked;
  }
});

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

Learn more about recurring payments and consent management in the Recurring payments guide.

Add payment sheet interactions

Implement dynamic price updates based on shipping selections. See Configuration for comprehensive implementation examples.

const config = {
  // ... other configuration
  paymentDataRequest: {
    // ... other payment data request config
    callbackIntents: ['SHIPPING_ADDRESS', 'SHIPPING_OPTION'],
    emailRequired: true,
    shippingAddressRequired: true,
    shippingOptionRequired: true,
    shippingAddressParameters: {
      allowedCountryCodes: ['US', 'CA', 'DE', 'FR', 'GB'],
      phoneNumberRequired: true
    },
    shippingOptionParameters: {
      defaultSelectedOptionId: 'standard',
      shippingOptions: [
        {
          id: 'standard',
          label: 'Standard Shipping',
          description: '5-7 business days'
        },
        {
          id: 'express',
          label: 'Express Shipping',
          description: '2-3 business days'
        }
      ]
    }
  },
  onPaymentDataChanged: async (intermediatePaymentData) => {
    console.log('Payment data changed:', intermediatePaymentData);
    
    // Calculate new total based on shipping
    const baseAmount = 20.00;
    const tax = 5.00;
    const shippingCost = calculateShipping(intermediatePaymentData.shippingAddress);
    const newTotal = baseAmount + tax + shippingCost;
    
    // Update the SDK amount
    pxpSdk.updateAmount(newTotal);
    
    // Return updated transaction info
    return {
      newTransactionInfo: {
        totalPriceStatus: 'FINAL',
        totalPrice: newTotal.toFixed(2),
        totalPriceLabel: 'Total',
        displayItems: [
          {
            label: 'Product',
            type: 'LINE_ITEM',
            price: '20.00'
          },
          {
            label: 'Tax',
            type: 'TAX',
            price: '5.00'
          },
          {
            label: 'Shipping',
            type: 'SHIPPING_OPTION',
            price: shippingCost.toFixed(2)
          }
        ]
      }
    };
  }
};

For detailed implementation patterns, validation logic, and best practices, see the Payment sheet interactions section in Configuration.

Add custom validation

Implement custom validation before opening the Google Pay payment sheet. See Events for all available event handlers.

const config = {
  // ... other configuration
  onCustomValidation: async () => {
    // Validate other form fields
    const isValid = validateCheckoutForm();
    
    if (!isValid) {
      showValidationErrors();
      return false; // Prevent Google Pay sheet from opening
    }
    
    return true; // Allow Google Pay sheet to open
  },
  onGooglePaymentButtonClicked: async (event) => {
    // Handle button click
    console.log('Google Pay button clicked');
    // Optionally perform actions before payment sheet opens
  }
};

Handle soft declines and retry

Implement retry logic for soft decline scenarios. For complete 3DS retry patterns, see 3D Secure.

const config = {
  // ... other configuration
  onPreRetrySoftDecline: (data) => {
    console.log('Soft decline detected', data);
    console.log('merchantTransactionId', data.merchantTransactionId);
    console.log('systemTransactionId', data.systemTransactionId);

    // Return updated authentication configuration for retry, return null for not retry
    return {
      retry: true,
      updatedConfigs: {
        onPreInitiateAuthentication: (): PreInitiateIntegratedAuthenticationData => {
          return {
            providerId: "pxpfinancial",
            timeout: 12
          };
        },
        onPreAuthentication: async () => {
          return {
            merchantCountryNumericCode: "840",
            merchantLegalName: "Your Store name",
            challengeWindowSize: 4,
            requestorChallengeIndicator: "02",
          };
        },
      }
    };
  }
};

Complete example

Here's a complete React implementation showing how to integrate the Google Pay button component:

import { useEffect, useState } from "react";
import { PxpCheckout, GooglePayTransactionInitData, BaseSubmitResult } from "@pxpio/web-components-sdk";

export default function GooglePayCheckout() {
  const [payAmount] = useState<number>(25);
  const [currencyCode] = useState<string>("EUR");
  const [sessionData, setSessionData] = useState<any>();
  const [errorMessage, setErrorMessage] = useState<string>("");
  const [successMessage, setSuccessMessage] = useState<string>("");

  // 1. Create the session data from your backend
  useEffect(() => {
    const createSession = async () => {
      try {
        // Fetch session data from your backend API
        const response = await fetch('/api/create-session', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            amount: payAmount,
            currency: currencyCode
          })
        });
        const data = await response.json();
        setSessionData(data);
      } catch (error) {
        console.error('Failed to create session:', error);
        setErrorMessage('Failed to initialize payment');
      }
    };
    createSession();
  }, [payAmount, currencyCode]);

  // 2. Initialize the SDK, create and mount component
  useEffect(() => {
    if (!sessionData) return;

    // 2.1 Initialize the SDK
    const paymentSDK = PxpCheckout.initialize({
      environment: 'test',
      session: sessionData,
      ownerId: 'your-owner-id',
      ownerType: 'MerchantGroup',
      kountDisabled: false, // OPTIONAL: Set to true to disable Kount fraud detection
      transactionData: {
        currency: currencyCode,
        amount: payAmount,
        entryType: 'Ecom',
        intent: {
          card: 'Authorisation',
        },
        merchantTransactionId: crypto.randomUUID(),
        merchantTransactionDate: () => new Date().toISOString(),
      },
      onGetShopper: async () => {
        return {
          id: 'shopper-' + Date.now(),
          email: 'customer@example.com',
          firstName: 'John',
          lastName: 'Doe'
        };
      }
    });

    // 2.2 Create the Google Pay button component
    const googlePayButton = paymentSDK.create('google-pay-button', {
      paymentDataRequest: {
        allowedPaymentMethods: [{
          type: 'CARD',
          parameters: {
            allowedCardNetworks: ['VISA', 'MASTERCARD', 'AMEX'],
            allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
          }
        }],
        transactionInfo: {
          currencyCode: currencyCode,
          totalPriceStatus: 'FINAL',
          totalPrice: payAmount.toFixed(2),
          totalPriceLabel: 'Total',
          displayItems: [
            {
              label: 'Product',
              type: 'LINE_ITEM',
              price: '20.00',
              status: 'FINAL'
            },
            {
              label: 'Tax',
              type: 'TAX',
              price: '5.00',
              status: 'FINAL'
            }
          ],
        },
      },
      style: {
        type: 'buy',
        color: 'default',
        height: '48px',
        borderRadius: 4,
        sizeMode: 'fill'
      },
      onPreAuthorisation: async (): Promise<GooglePayTransactionInitData> => {
        console.log('Pre-authorisation started');
        setErrorMessage('');
        setSuccessMessage('Processing payment...');
        
        return {
          riskScreeningData: {
            performRiskScreening: true
          }
        };
      },
      onPostAuthorisation: async (result: BaseSubmitResult | null) => {
        console.log('Payment completed:', result);
        
        if (result && 'merchantTransactionId' in result) {
          // Success - MerchantSubmitResult
          console.log('Merchant Transaction ID:', result.merchantTransactionId);
          console.log('System Transaction ID:', result.systemTransactionId);
          
          setSuccessMessage('Payment successful! Redirecting...');
          setErrorMessage('');
          
          // Use merchantTransactionId & systemTransactionId to get transaction details from Unity
          setTimeout(() => {
            window.location.href = '/payment-success?txn=' + result.merchantTransactionId;
          }, 2000);
        } else {
          setErrorMessage('Payment failed: ' + (result?.errorReason || 'Please try again'));
          setSuccessMessage('');
        }
      },
      onError: (error) => {
        console.error('Google Pay error:', error);
        setErrorMessage('Payment error: ' + error.message);
        setSuccessMessage('');
      },
      onCancel: () => {
        console.log('Payment cancelled by user');
        setErrorMessage('Payment was cancelled');
        setSuccessMessage('');
      }
    });

    // 2.3 Mount the component
    googlePayButton.mount('google-pay-container');

    // 2.4 Cleanup on unmount
    return () => {
      googlePayButton.unmount();
    };
  }, [sessionData, payAmount, currencyCode]);

  return (
    <div style={{ maxWidth: '400px', margin: '50px auto', padding: '20px', fontFamily: 'sans-serif' }}>
      <h2>Complete your purchase</h2>
      
      <div style={{ background: '#f8f9fa', padding: '20px', borderRadius: '8px', marginBottom: '20px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '10px' }}>
          <span>Product</span>
          <span>€20.00</span>
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '10px' }}>
          <span>Tax</span>
          <span>€5.00</span>
        </div>
        <hr style={{ border: 'none', borderTop: '1px solid #ddd', margin: '15px 0' }} />
        <div style={{ display: 'flex', justifyContent: 'space-between', fontWeight: 600, fontSize: '18px' }}>
          <span>Total</span>
          <span>€25.00</span>
        </div>
      </div>

      {errorMessage && (
        <div style={{ background: '#ffebee', color: '#c62828', padding: '12px', borderRadius: '4px', marginBottom: '10px' }}>
          {errorMessage}
        </div>
      )}
      
      {successMessage && (
        <div style={{ background: '#e8f5e8', color: '#2e7d32', padding: '12px', borderRadius: '4px', marginBottom: '10px' }}>
          {successMessage}
        </div>
      )}

      {/* Google Pay button will be mounted here */}
      <div id="google-pay-container" style={{ margin: '20px 0' }}></div>

      <div style={{ textAlign: 'center', color: '#666', margin: '20px 0' }}>
        <p>Or pay with credit card</p>
      </div>
    </div>
  );
}