# Events

Implement callbacks to customise your Google Pay payment flow for Web.

## Overview

Components emit events based on user interaction or validation. You can use these to implement callback functions, which allow you to inject your own business logic and user experience customisations into the payment flow at critical moments. They ensure that while the SDK handles the complex technical aspects of payment processing, you retain full control over the customer experience and can seamlessly integrate payments into your broader business workflows and systems.

Callbacks enable you to:

* Validate business rules before payments proceed.
* Display custom error, failure, or success messages.
* Tailor user interfaces to match your brand's look and feel.
* Integrate with your own systems for fraud detection or customer management.
* Control exactly how your customers experience both successful and failed transactions.
* Handle Google Pay specific requirements like shipping calculations and dynamic price updates.
* Integrate 3D Secure authentication for enhanced security.
* Manage consent for payment tokens and recurring payments.


## SDK data callbacks

The Google Pay component requires an SDK initialisation callback to gather shopper information during transaction processing:

* **`onGetShopper` (required when using consent tokens):** Called to retrieve current shopper data (ID, email, name, etc.) for transaction submission to the backend and token association.


This callback ensures the Google Pay component always uses the latest customer data from your application state, forms, or APIs when processing payments and storing payment tokens.

## Google Pay-specific events

All Google Pay-specific events are optional and can be mixed and matched based on your business needs.

The SDK initialisation callback (`onGetShopper`) works alongside Google Pay-specific callbacks to provide a complete payment experience. SDK callbacks handle backend transaction data, while Google Pay callbacks handle the payment sheet interactions and transaction flow.

## Payment flow events

### onPreAuthorisation

This callback is triggered before payment authorisation, allowing you to provide transaction initialisation data or control whether to proceed.

You can use it to:

* Integrate with Kount or other fraud detection services.
* Apply business rules based on transaction amount or customer history.
* Check product availability before processing payment.
* Provide risk screening data for fraud prevention.
* Configure PSD2 SCA exemptions for the transaction.
* Cancel the payment if business rules aren't met.


#### Event data

| Event data | Description |
|  --- | --- |
| `data`PreAuthorizationData | Pre-authorisation data including gateway token ID. |
| `data.gatewayTokenId`string | The gateway token identifier for the payment. |


#### Return value

| Return value | Description |
|  --- | --- |
| `GooglePayTransactionInitData`object | Transaction initialisation data to proceed with payment. |
| `GooglePayTransactionInitData.psd2Data`object | PSD2 data including SCA exemption configuration. |
| `GooglePayTransactionInitData.psd2Data.scaExemption`string | SCA exemption type: `AnonymousCard`, `LowValue`, `SecureCorporate`, `TransactionRiskAnalysis`, `TrustedBeneficiary`. |
| `GooglePayTransactionInitData.riskScreeningData`object | Risk screening data for fraud detection. |
| `GooglePayTransactionInitData.riskScreeningData.performRiskScreening`boolean | Whether to perform risk screening. |
| `GooglePayTransactionInitData.riskScreeningData.excludeDeviceData`boolean | Whether to exclude device data from risk screening. |
| `GooglePayTransactionInitData.riskScreeningData.userIp`string | The customer's IPv4 address in dotted decimal notation. |
| `GooglePayTransactionInitData.riskScreeningData.account`object | Account information for the user. |
| `GooglePayTransactionInitData.riskScreeningData.account.id`string | Merchant-provided unique account ID of the user. |
| `GooglePayTransactionInitData.riskScreeningData.account.creationDateTime`string | ISO 8601 date and time when the user's account was created. |
| `GooglePayTransactionInitData.riskScreeningData.fulfillments`array | Array of fulfillment information. |
| `GooglePayTransactionInitData.riskScreeningData.fulfillments[].type`string | Fulfillment type: `Shipped`, `Digital`, `StorePickUp`, `LocalDelivery`, `StoreDriveUp`, `InPerson`. |
| `GooglePayTransactionInitData.riskScreeningData.fulfillments[].shipping.shippingMethod`string | Shipping method: `Standard`, `Express`, `SameDay`, `NextDay`, `SecondDay`. |
| `GooglePayTransactionInitData.riskScreeningData.fulfillments[].recipientPerson.phoneNumber`string | Phone number of the recipient. |
| `null` | Return null to cancel the authorisation. |


#### Example implementation


```typescript
const googlePayConfig = {
  onPreAuthorisation: async (data) => {
    console.log('Pre-authorisation started');
    console.log('Gateway Token ID:', data.gatewayTokenId);
    
    // Perform pre-payment validation
    const isHighRisk = await checkCustomerRiskProfile();
    const customerTier = await getCustomerTier();
    
    // Check inventory before proceeding
    const inventoryAvailable = await checkInventory();
    if (!inventoryAvailable) {
      showError('Some items are no longer available');
      return null; // Cancel payment
    }
    
    // Check business rules
    const cartTotal = getCurrentCartTotal();
    if (cartTotal > 10000 && isHighRisk) {
      showError('Transaction requires additional verification');
      return null; // Cancel payment for manual review
    }
    
    return {
      riskScreeningData: {
        performRiskScreening: true,
        excludeDeviceData: false,
        userIp: "192.168.1.100",
        account: {
          id: "user_12345678",
          creationDateTime: "2024-01-15T10:30:00.000Z"
        },
        fulfillments: [{
          type: "Shipped",
          recipientPerson: {
            phoneNumber: "+1234567890"
          }
        }]
      },
      psd2Data: {
        scaExemption: cartTotal < 30 ? 'LowValue' : 'TransactionRiskAnalysis'
      }
    };
  }
};
```

### onPostAuthorisation

This callback is triggered after the payment is authorised, providing the transaction result and payment data.

You can use it to:

* Handle successful or declined payments.
* Redirect customers to success or failure pages.
* Update inventory and order status.
* Send order confirmation emails.
* Record transactions for business intelligence.
* Display custom success or error messages.
* Process shipping information collected from Google Pay.
* Save transaction details for customer account history.


#### Event data

| Event data | Description |
|  --- | --- |
| `result`BaseSubmitResult | The transaction result - either `MerchantSubmitResult` (success) or `FailedSubmitResult` (failure). |
| `result.merchantTransactionId`string | Your unique transaction identifier (present in both success and failure). |
| `result.systemTransactionId`string | The PXP system transaction identifier (only in `MerchantSubmitResult`). |
| `result.errorCode`string | Error code (only in `FailedSubmitResult`). |
| `result.errorReason`string | Error reason description (only in `FailedSubmitResult`). |
| `result.correlationId`string | Correlation ID for tracking (only in `FailedSubmitResult`). |
| `paymentData`AuthorisationPaymentData | The Google Pay payment data collected during checkout. |
| `paymentData.Email`string | Customer email address (if email collection was enabled). |
| `paymentData.ShippingAddress`object | Shipping address (if shipping address collection was enabled). |
| `paymentData.ShippingOption`object | Selected shipping option (if shipping options were provided). |


#### Example implementation


```typescript
const googlePayConfig = {
  onPostAuthorisation: async (result, paymentData) => {
    console.log('Payment completed:', result);
    
    if (result && 'merchantTransactionId' in result && !('errorCode' in result)) {
      // Success - MerchantSubmitResult
      console.log('Payment authorised successfully');
      console.log('Transaction ID:', result.merchantTransactionId);
      console.log('System Transaction ID:', result.systemTransactionId);
      
      // Extract payment data
      const email = paymentData.Email;
      const shippingAddress = paymentData.ShippingAddress;
      const shippingOption = paymentData.ShippingOption;
      
      // Update inventory
      await updateInventory(result.merchantTransactionId);
      
      // Create order record
      await createOrder({
        transactionId: result.merchantTransactionId,
        email: email,
        shippingAddress: shippingAddress,
        shippingMethod: shippingOption?.id,
        status: 'confirmed'
      });
      
      // Send confirmation email
      await sendConfirmationEmail({
        transactionId: result.merchantTransactionId,
        email: email,
        shippingAddress: shippingAddress
      });
      
      // Track successful payment
      trackEvent('google-pay-success', {
        transactionId: result.merchantTransactionId,
        cartValue: getCurrentCartValue(),
        shippingMethod: shippingOption?.id
      });
      
      // Update customer account
      await updateCustomerPurchaseHistory(result.merchantTransactionId);
      
      // Redirect to success page
      window.location.href = `/payment-success?txn=${result.merchantTransactionId}`;
      
    } else if (result && 'errorCode' in result) {
      // Failure - FailedSubmitResult
      console.error('Payment declined:', result.errorReason);
      console.error('Error code:', result.errorCode);
      console.error('Correlation ID:', result.correlationId);
      
      // Log failure for monitoring
      logPaymentFailure({
        errorCode: result.errorCode,
        errorReason: result.errorReason,
        correlationId: result.correlationId,
        merchantTransactionId: result.merchantTransactionId
      });
      
      // Show user-friendly error message
      const errorMessage = getErrorMessage(result.errorCode);
      showError(`Payment declined: ${errorMessage}`);
      
      // Track failed payment
      trackEvent('google-pay-declined', {
        errorCode: result.errorCode,
        correlationId: result.correlationId
      });
      
      // Offer alternatives
      showAlternativePaymentMethods();
      
    } else {
      // Payment exception
      console.error('Payment exception:', result);
      showError('Payment processing error. Please contact support.');
      trackEvent('google-pay-exception', { result });
    }
  }
};
```

## Google Pay sheet interaction events

### onPaymentDataChanged

This callback is triggered when payment data changes in the Google Pay sheet, such as shipping address or shipping option selection.

You can use it to:

* Calculate shipping costs based on selected address.
* Update transaction amounts dynamically.
* Validate addresses and show errors for unserviceable locations.
* Add or remove line items based on selections.
* Calculate taxes based on shipping destination.
* Apply discounts based on shipping method selection.
* Check delivery availability for specific locations.


When updating transaction amounts, use the SDK's `updateAmount()` method to synchronise the amount with the backend session.

#### Event data

| Event data | Description |
|  --- | --- |
| `intermediatePaymentData`google.payments.api.IntermediatePaymentData | The intermediate payment data from Google Pay. |
| `intermediatePaymentData.callbackTrigger`string | The trigger that caused the callback: `INITIALIZE`, `SHIPPING_ADDRESS`, `SHIPPING_OPTION`, `OFFER`. |
| `intermediatePaymentData.shippingAddress`object | The selected shipping address (available when `callbackTrigger` is `SHIPPING_ADDRESS`). |
| `intermediatePaymentData.shippingAddress.countryCode`string | The ISO country code. |
| `intermediatePaymentData.shippingAddress.postalCode`string | The postal code. |
| `intermediatePaymentData.shippingAddress.administrativeArea`string | The state or province. |
| `intermediatePaymentData.shippingAddress.locality`string | The city or locality. |
| `intermediatePaymentData.shippingOptionData`object | The selected shipping option (available when `callbackTrigger` is `SHIPPING_OPTION`). |
| `intermediatePaymentData.shippingOptionData.id`string | The shipping option identifier. |


#### Return value

| Return value | Description |
|  --- | --- |
| `PaymentDataRequestUpdate`object | Updated payment data to display in the Google Pay sheet. |
| `PaymentDataRequestUpdate.newTransactionInfo`object | Updated transaction information including total and line items. |
| `PaymentDataRequestUpdate.newTransactionInfo.totalPriceStatus`string | Price status: `FINAL` or `ESTIMATED`. |
| `PaymentDataRequestUpdate.newTransactionInfo.totalPrice`string | Updated total price as a string. |
| `PaymentDataRequestUpdate.newTransactionInfo.displayItems`array | Array of line items to display. |
| `PaymentDataRequestUpdate.newShippingOptionParameters`object | Updated shipping options based on address. |
| `PaymentDataRequestUpdate.error`object | Error information for invalid addresses or options. |
| `PaymentDataRequestUpdate.error.reason`string | Error reason code: `SHIPPING_ADDRESS_INVALID`, `SHIPPING_ADDRESS_UNSERVICEABLE`, `SHIPPING_OPTION_INVALID`. |
| `PaymentDataRequestUpdate.error.message`string | User-friendly error message. |
| `PaymentDataRequestUpdate.error.intent`string | The callback intent that triggered the error: `SHIPPING_ADDRESS`, `SHIPPING_OPTION`. |


#### Example implementation


```typescript
const googlePayConfig = {
  onPaymentDataChanged: async (intermediatePaymentData) => {
    console.log('Payment data changed:', intermediatePaymentData.callbackTrigger);
    
    const baseAmount = 20.00;
    let tax = 5.00;
    let shippingCost = 0;
    let error = null;
    
    // Handle shipping address change
    if (intermediatePaymentData.callbackTrigger === 'SHIPPING_ADDRESS') {
      const address = intermediatePaymentData.shippingAddress;
      
      // Validate address completeness
      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 location
      else if (!['US', 'CA', 'GB'].includes(address.countryCode)) {
        error = {
          reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
          message: `We do not ship to ${address.countryCode}`,
          intent: 'SHIPPING_ADDRESS'
        };
      }
      // Calculate shipping and tax for valid address
      else {
        // Calculate shipping cost based on location
        shippingCost = await calculateShipping(address);
        
        // Calculate tax based on destination
        tax = await calculateTax(address, baseAmount);
        
        console.log('Shipping calculated:', { shippingCost, tax });
      }
    }
    
    // Handle shipping option change
    if (intermediatePaymentData.callbackTrigger === 'SHIPPING_OPTION') {
      const optionId = intermediatePaymentData.shippingOptionData.id;
      
      // Apply different shipping rates
      const shippingRates = {
        'shipping_standard': 5.00,
        'shipping_express': 15.00,
        'shipping_overnight': 25.00
      };
      
      shippingCost = shippingRates[optionId] || 5.00;
      console.log('Shipping option selected:', optionId, shippingCost);
    }
    
    // Calculate new total
    const newTotal = baseAmount + tax + shippingCost;
    
    // Update SDK amount (synchronizes with backend)
    await pxpSdk.updateAmount(newTotal);
    
    // Return updated payment data
    return {
      newTransactionInfo: {
        totalPriceStatus: 'FINAL',
        totalPrice: newTotal.toFixed(2),
        totalPriceLabel: 'Total',
        displayItems: [
          {
            label: 'Subtotal',
            type: 'LINE_ITEM',
            price: baseAmount.toFixed(2)
          },
          {
            label: 'Tax',
            type: 'TAX',
            price: tax.toFixed(2)
          },
          {
            label: 'Shipping',
            type: 'LINE_ITEM',
            price: shippingCost.toFixed(2)
          }
        ]
      },
      ...(error && { error })
    };
  }
};
```

### onGooglePaymentButtonClicked

This callback is triggered when the Google Pay button is clicked, before the payment sheet opens.

You can use it to:

* Track button click events for analytics.
* Perform final validation before opening payment sheet.
* Show loading indicators.
* Prepare data for the payment flow.
* Update cart totals or recalculate amounts.
* Check inventory availability one last time.


#### Event data

| Event data | Description |
|  --- | --- |
| `event`Event | The DOM click event. |


#### Example implementation


```typescript
const googlePayConfig = {
  onGooglePaymentButtonClicked: async (event) => {
    console.log('Google Pay button clicked');
    
    // Track button click
    trackEvent('google-pay-button-clicked', {
      timestamp: new Date().toISOString(),
      cartValue: getCurrentCartValue(),
      itemCount: getCartItemCount()
    });
    
    // Show loading indicator
    showLoadingIndicator();
    
    // Final inventory check
    const inventoryOk = await checkInventory();
    if (!inventoryOk) {
      hideLoadingIndicator();
      showError('Some items are no longer available. Please review your cart.');
      event.preventDefault();
      return;
    }
    
    // Prepare checkout data
    await prepareCheckoutData();
    
    // Update cart totals
    await recalculateCartTotals();
    
    hideLoadingIndicator();
  }
};
```

## Validation and error events

### onCustomValidation

This callback is executed before opening the Google Pay payment sheet, allowing custom validation of other form fields.

You can use it to:

* Validate required form fields before payment.
* Check terms and conditions acceptance.
* Verify customer information completeness.
* Prevent payment sheet from opening if validation fails.
* Validate minimum order amounts.
* Check age verification requirements.


#### Event data

This callback receives no parameters.

#### Return value

| Return value | Description |
|  --- | --- |
| `boolean` | Return `true` to proceed with opening payment sheet, `false` to prevent opening. |


#### Example implementation


```typescript
const googlePayConfig = {
  onCustomValidation: async () => {
    // Validate email
    const email = document.getElementById('email')?.value;
    if (!email || !email.includes('@')) {
      showError('Please enter a valid email address');
      highlightField('email');
      return false;
    }
    
    // Validate phone number (if required)
    const phone = document.getElementById('phone')?.value;
    if (!phone || phone.length < 10) {
      showError('Please enter a valid phone number');
      highlightField('phone');
      return false;
    }
    
    // Validate terms acceptance
    const termsAccepted = document.getElementById('terms')?.checked;
    if (!termsAccepted) {
      showError('Please accept the terms and conditions');
      highlightField('terms');
      return false;
    }
    
    // Validate age verification (if required)
    const ageVerified = document.getElementById('age-verify')?.checked;
    if (!ageVerified) {
      showError('You must be 18 or older to complete this purchase');
      return false;
    }
    
    // Validate minimum order amount
    const cartTotal = getCurrentCartTotal();
    if (cartTotal < 10.00) {
      showError('Minimum order amount is $10.00');
      return false;
    }
    
    // Final inventory check
    const inventoryAvailable = await checkInventory();
    if (!inventoryAvailable) {
      showError('Some items are no longer available. Please review your cart.');
      return false;
    }
    
    return true; // All validations passed
  }
};
```

### onError

This callback is triggered when an error occurs during the Google Pay payment process.

You can use it to:

* Log errors for debugging and monitoring.
* Display user-friendly error messages.
* Offer alternative payment methods.
* Implement automatic retry for transient errors.
* Send error notifications to support teams.
* Track error rates for quality monitoring.


#### Event data

| Event data | Description |
|  --- | --- |
| `error`BaseSdkException | The SDK exception containing error details. |
| `error.name`string | The error type name (e.g., `GooglePayNotReadyException`). |
| `error.message`string | A human-readable error description. |
| `error.code`string | The error code for programmatic handling. |
| `error.stack`string | The stack trace for debugging. |


#### Example implementation


```typescript
const googlePayConfig = {
  onError: (error) => {
    console.error('Google Pay error:', error);
    
    // Log error for debugging
    logError('google-pay-error', {
      name: error.name,
      message: error.message,
      code: error.code,
      stack: error.stack,
      timestamp: new Date().toISOString(),
      userId: getCurrentUserId(),
      sessionId: getSessionId()
    });
    
    // Handle different error types
    if (error.name === 'GooglePayPaymentFailedException' && error.message.includes('cancel')) {
      // User cancelled - silent handling
      console.log('User cancelled Google Pay');
      trackEvent('google-pay-user-cancelled');
      return;
    } 
    
    if (error.name === 'GooglePayNotReadyException') {
      showUserMessage('Google Pay is not available on this device. Please use an alternative payment method.');
      showAlternativePaymentMethods();
      trackEvent('google-pay-not-available');
    } 
    
    else if (error.name === 'GooglePayPaymentFailedException' && error.message.includes('HTTPS')) {
      showUserMessage('Secure connection required. Please ensure your site uses HTTPS.');
      notifySupport('HTTPS requirement not met');
    }
    
    else if (error.name === 'GooglePayConfigurationValidationFailedException') {
      showUserMessage('Payment system temporarily unavailable. Please try again later.');
      notifySupport('Merchant configuration error', error);
    }
    
    else if (error.message.includes('Network')) {
      showUserMessage('Network error. Please check your connection and try again.');
      // Offer retry option
      showRetryButton();
    } 
    
    else {
      showUserMessage('Payment failed. Please try again or use a different payment method.');
      showAlternativePaymentMethods();
    }
    
    // Track error for monitoring
    trackEvent('google-pay-error', {
      errorType: error.name,
      errorCode: error.code
    });
  }
};
```

### onCancel

This callback is triggered when the customer cancels the Google Pay payment flow.

You can use it to:

* Track cancellation rates for conversion optimisation.
* Show helpful messages or alternative options.
* Save the customer's cart for later completion.
* Trigger email campaigns for abandoned checkouts.
* Understand drop-off points in the payment flow.
* Offer assistance or live chat support.


#### Event data

This callback receives no parameters.

#### Example implementation


```typescript
const googlePayConfig = {
  onCancel: () => {
    console.log('Google Pay cancelled');
    
    // Track cancellation for analytics
    trackEvent('google-pay-cancelled', {
      timestamp: new Date().toISOString(),
      cartValue: getCurrentCartValue(),
      itemCount: getCartItemCount(),
      pageUrl: window.location.href
    });
    
    // Preserve cart for later
    saveCartForLater();
    
    // Show helpful message
    showMessage(
      'No worries! Your items are saved. You can complete your purchase anytime.', 
      'info'
    );
    
    // Offer live chat support
    if (getCartValue() > 100) {
      showLiveChatOffer('Need help completing your purchase?');
    }
    
    // Offer alternatives after a short delay
    setTimeout(() => {
      showAlternativePaymentOptions();
    }, 2000);
    
    // Schedule abandoned cart email
    scheduleAbandonedCartEmail(
      getCustomerEmail(), 
      getCurrentCartData(),
      60 // Send after 60 minutes
    );
  }
};
```

## 3D Secure authentication events

Google Pay supports full 3DS authentication through the `AuthenticationConfig` interface. These events allow you to control the 3D Secure authentication flow.

For detailed 3DS configuration and implementation guidance, see the [3D Secure documentation](/guides/checkout/components/web/google-pay/3ds).

### useUnityAuthenticationStrategy

This configuration parameter controls which authentication strategy is used for 3DS authentication.

When set to `true`, Unity handles the authentication strategy automatically, and you aren't required to provide the following configuration parameters:

- `merchantCountryNumericCode` (for `onPreAuthentication`).
- `merchantLegalName` (for `onPreAuthentication`).
- `requestorChallengeIndicator` (for `onPreAuthentication`).
- `scaExemption` (for `onPreAuthorisation`).


Using Unity authentication strategy simplifies 3DS implementation by reducing the number of required configuration parameters. Unity will automatically provide optimal values based on the transaction context.

#### Configuration

| Configuration | Description |
|  --- | --- |
| `useUnityAuthenticationStrategy`boolean | When `true`, uses Unity authentication strategy and automatically handles merchant authentication configuration. When `false` or not specified, uses the default authentication strategy where merchant must provide all configuration.Default: `false` |


#### Example implementation

**With Unity authentication strategy (simplified):**


```typescript
const googlePayButton = pxpSdk.create('google-pay-button', {
  // ... other configuration
  
  // Use Unity authentication strategy
  useUnityAuthenticationStrategy: true,
  
  onPreInitiateAuthentication: () => {
    return {
      providerId: 'pxpfinancial',
      timeout: 12
    };
  },
  
  onPreAuthentication: async () => {
    // Simplified - no need to provide merchantCountryNumericCode, 
    // merchantLegalName, or requestorChallengeIndicator
    return {
      challengeWindowSize: 4
    };
  },
  
  onPreAuthorisation: async (data) => {
    // No need to provide scaExemption when using Unity strategy
    return {
      riskScreeningData: {
        performRiskScreening: true,
        userIp: "192.168.1.100",
        account: {
          id: "user_12345678",
          creationDateTime: "2024-01-15T10:30:00.000Z"
        },
        fulfillments: [{
          type: "Shipped",
          recipientPerson: {
            phoneNumber: "+1234567890"
          }
        }]
      }
    };
  }
});
```

**Without Unity authentication strategy (default):**


```typescript
const googlePayButton = pxpSdk.create('google-pay-button', {
  // ... other configuration
  
  // useUnityAuthenticationStrategy: false, // Default
  
  onPreInitiateAuthentication: () => {
    return {
      providerId: 'pxpfinancial',
      timeout: 12
    };
  },
  
  onPreAuthentication: async () => {
    // All parameters required when not using Unity strategy
    return {
      merchantCountryNumericCode: '840',
      merchantLegalName: 'Your Store',
      challengeWindowSize: 4,
      requestorChallengeIndicator: '02'
    };
  },
  
  onPreAuthorisation: async (data) => {
    return {
      riskScreeningData: {
        performRiskScreening: true,
        userIp: "192.168.1.100",
        account: {
          id: "user_12345678",
          creationDateTime: "2024-01-15T10:30:00.000Z"
        },
        fulfillments: [{
          type: "Shipped",
          recipientPerson: {
            phoneNumber: "+1234567890"
          }
        }]
      },
      psd2Data: {
        scaExemption: 'LowValue'
      }
    };
  }
});
```

### onPreInitiateAuthentication

This callback is triggered before initiating the 3DS pre-authentication check.

You can use it to:

* Configure 3DS provider settings.
* Set authentication request preferences.
* Specify timeout values for authentication requests.
* Control the initial authentication flow.


#### Event data

This callback receives no parameters.

#### Return value

| Return value | Description |
|  --- | --- |
| `PreInitiateAuthenticationConfig`object | Configuration for 3DS pre-authentication. |
| `PreInitiateAuthenticationConfig.providerId`string | Your 3DS provider identifier. Optional. |
| `PreInitiateAuthenticationConfig.requestorAuthenticationIndicator`string | The authentication indicator. Optional.Possible values:`01` (Payment transaction)`02` (Recurring transaction)`03` (Installment transaction)`04` (Add card)`05` (Maintain card)`06` (Cardholder verification)`07` (Billing agreement)`08` (Split shipment)`09` (Delayed shipment)`10` (Split payment) |
| `PreInitiateAuthenticationConfig.timeout`number | The timeout to get the fingerprint result, in seconds (max 600). Optional. |


#### Example implementation


```typescript
const googlePayConfig = {
  onPreInitiateAuthentication: () => {
    console.log('Initiating 3DS pre-authentication');
    
    // Track 3DS initiation
    trackEvent('3ds-pre-initiate');
    
    return {
      providerId: 'your-3ds-provider-id', // Optional - your 3DS provider identifier
      requestorAuthenticationIndicator: '01', // Payment transaction
      timeout: 300 // 5 minutes (300 seconds)
    };
  }
};
```

### onPostInitiateAuthentication

This callback is triggered after the 3DS pre-authentication check completes.

You can use it to:

* Retrieve authentication assessment results.
* Determine if full authentication is required.
* Log authentication initiation for compliance.
* Check SCA requirements and exemptions.
* Decide whether to proceed with challenge.


#### Event data

| Event data | Description |
|  --- | --- |
| `data`AuthenticationResult | Object containing the authentication initiation result. |
| `data.authenticationId`string | The unique identifier for this 3DS session. Use this ID to retrieve full PreInitiateAuthentication result from the Unity backend. |


Use the `authenticationId` with the [Get 3DS pre-initiate authentication details](/apis/three-d-secure-authentication/integrated-authentication/get-preinitiate-authentication-details) API to retrieve pre-authentication results including SCA mandates, exemptions, and 3DS support.

#### Example implementation


```typescript
const googlePayConfig = {
  onPostInitiateAuthentication: async (data) => {
    console.log('3DS pre-initiation completed. Authentication ID:', data.authenticationId);
    
    // Retrieve initiate authentication result from Unity
    const authResult = await fetchAuthenticationResultFromBackend(data.authenticationId);
    
    // Check if authentication setup was successful
    if (authResult.state === 'PendingClientData') {
      console.log('Waiting for client data collection');
    }
    
    // Check if SCA (Strong Customer Authentication) is mandated
    if (authResult.scaMandated) {
      console.log('SCA is required - 3DS must complete successfully');
      showMessage('Additional verification required');
    }
    
    // Check available exemptions
    if (authResult.applicableExemptions === 'LVP') {
      console.log('Low value exemption available');
    } else if (authResult.applicableExemptions === 'TRA') {
      console.log('Transaction risk analysis exemption available');
    }
    
    // Evaluate the result to update authentication decision
    const decision = await evaluateAuthenticationAndUpdateSession(authResult);
  }
};
```

### onPreAuthentication

This callback is triggered before full 3DS authentication begins, allowing you to provide authentication configuration.

You can use it to:

* Specify merchant details for authentication.
* Configure challenge window preferences.
* Set challenge indicators based on risk assessment.
* Provide country-specific merchant information.
* Control the authentication user experience.


#### Event data

This callback receives no parameters.

#### Return value

| Return value | Description |
|  --- | --- |
| `PreAuthenticationConfig`object | Configuration for 3DS authentication. |
| `PreAuthenticationConfig.merchantCountryNumericCode`string | Your merchant location, as an ISO numeric country code (e.g., `840` for USA, `826` for UK). Optional. |
| `PreAuthenticationConfig.merchantLegalName`string | Your legal name. Optional. |
| `PreAuthenticationConfig.challengeWindowSize`number | **Required.** The challenge window size.Possible values:`1` (250x400)`2` (390x400)`3` (500x600)`4` (600x400)`5` (Full screen) |
| `PreAuthenticationConfig.requestorChallengeIndicator`string | The challenge preference indicator. Optional.Possible values:`01` (No preference)`02` (No challenge requested)`03` (Challenge requested: Merchant preference)`04` (Challenge requested: Mandate)`05` (No challenge requested: TRA performed)`10` (No challenge requested: Low value exemption) |
| `PreAuthenticationConfig.timeout`number | The timeout to get the challenge result, in seconds (max 600). Optional. |
| `null` | Return null to skip the authentication. |


#### Example implementation


```typescript
const googlePayConfig = {
  onPreAuthentication: async () => {
    console.log('Starting 3DS authentication');
    
    // Get merchant configuration
    const merchantConfig = await getMerchantConfiguration();
    
    // Assess transaction risk
    const riskLevel = await assessTransactionRisk();
    
    // Determine challenge indicator based on risk
    let challengeIndicator = '01'; // No preference
    if (riskLevel === 'high') {
      challengeIndicator = '04'; // Challenge required
    } else if (riskLevel === 'low') {
      challengeIndicator = '02'; // No challenge requested
    }
    
    // Track authentication start
    trackEvent('3ds-authentication-started', {
      riskLevel,
      challengeIndicator
    });
    
    return {
      merchantCountryNumericCode: '840', // USA
      merchantLegalName: merchantConfig.legalName,
      challengeWindowSize: 5, // Full screen for best UX
      requestorChallengeIndicator: challengeIndicator,
      timeout: 600 // 10 minutes (600 seconds) - max value
    };
  }
};
```

### onPostAuthentication

This callback is triggered after 3DS authentication completes, providing the authentication result.

You can use it to:

* Retrieve authentication results.
* Log authentication outcomes for compliance.
* Track authentication success rates.
* Handle authentication failures gracefully.
* Record ECI values and authentication indicators.


#### Event data

| Event data | Description |
|  --- | --- |
| `data`AuthenticationResult | Object containing authentication result. |
| `data.authenticationId`string | The unique identifier for the authentication attempt. Use this ID to retrieve full authentication details from the Unity backend. |


Use the `authenticationId` with the [Get 3DS authentication details](/apis/three-d-secure-authentication/integrated-authentication/get-authentication-details) API to retrieve the full authentication results including transaction status, ECI values, and CAVV data.

#### Example implementation


```typescript
const googlePayConfig = {
  onPostAuthentication: async (data) => {
    console.log('3DS authentication completed. Authentication ID:', data.authenticationId);
    
    // Send authenticationId to backend to retrieve authentication result
    const authResult = await fetchAuthenticationResultFromBackend(data.authenticationId);
    
    console.log('Authentication result:', authResult);
    
    // Check transaction status
    switch (authResult.transactionStatus) {
      case 'Y':
        console.log('Authentication successful - no challenge needed');
        showMessage('Card verified successfully');
        break;
        
      case 'C':
        console.log('Challenge completed - checking result...');
        if (authResult.state === 'AuthenticationSuccessful') {
          console.log('Challenge completed successfully');
          showMessage('Verification completed');
        } else {
          console.log('Challenge failed');
          showError('Verification failed. Please try again.');
          return; // Stop payment
        }
        break;
        
      case 'N':
        console.log('Authentication failed');
        showError('Card verification failed');
        return; // Stop payment
        
      case 'R':
        console.log('Authentication rejected');
        showError('Payment was rejected by your bank');
        return; // Stop payment
    }
    
    // Evaluate authentication result to update authorisation decision
    const authorisationDecision = await evaluateAuthenticationAndUpdateAuthorization(authResult);
    
    console.log('Proceeding to final authorisation...');
  }
};
```

## Consent and token events

### onGetConsent

This callback is called to determine if the customer has given consent to store their payment token for future use.

The primary use case is to set a default consent value (`true` or `false`) when you're not rendering the `google-pay-consent` checkbox component. This allows you to control token storage behaviour without requiring explicit customer interaction.

When both `googlePayConsentComponent` and `onGetConsent` are provided, the consent component value takes priority. The `onGetConsent` callback won't be called if a consent component is linked.

You can use it to:

* Set a default consent value when not using the consent checkbox component.
* Check custom consent checkbox status from your own UI.
* Verify terms and conditions acceptance for stored payments.
* Control token storage behaviour based on business logic.
* Implement opt-in/opt-out for recurring payments.
* Respect customer privacy preferences.


#### Event data

This callback receives no parameters.

#### Return value

| Return value | Description |
|  --- | --- |
| `boolean` | Return `true` if consent was given, `false` otherwise. |


#### Example implementation

**Example 1: Set default consent value without checkbox**


```typescript
const googlePayButton = pxpSdk.create('google-pay-button', {
  // ... payment configuration
  
  onGetConsent: () => {
    // Return a default value when not using consent checkbox
    // Set to true to always store tokens, false to never store
    return true; // Always save payment method for this merchant
  },
  
  onPostAuthorisation: (result, paymentData) => {
    if (result && 'merchantTransactionId' in result) {
      console.log('Payment successful, token will be stored');
      // Token storage is handled automatically based on consent
    }
  }
});
```

**Example 2: Use custom consent checkbox from your UI**


```typescript
const googlePayButton = pxpSdk.create('google-pay-button', {
  // ... payment configuration
  
  onGetConsent: () => {
    // Check your own custom checkbox
    const consentGiven = document.getElementById('save-payment-checkbox')?.checked || false;
    
    if (consentGiven) {
      console.log('Customer consented to save payment method');
      
      // Track consent
      trackEvent('payment-consent-given', {
        timestamp: new Date().toISOString(),
        paymentMethod: 'google-pay'
      });
    } else {
      console.log('Customer did not consent to save payment method');
    }
    
    return consentGiven;
  }
});
```

**Example 3: With consent component (component value takes priority)**


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

const googlePayButton = pxpSdk.create('google-pay-button', {
  // ... payment configuration
  
  // Link the consent component
  googlePayConsentComponent: googlePayConsent,
  
  // This callback will NOT be called because consent component is linked
  onGetConsent: () => {
    return true; // This is ignored when consent component is present
  }
});

// The consent component's checked state will be used for token storage
```

## Risk screening events

### onKountCollectStart

This callback is triggered when Kount fraud screening data collection begins.

You can use it to:

* Track fraud screening initiation.
* Show loading indicators during screening.
* Log screening events for compliance.
* Monitor fraud detection performance.


#### Event data

This callback receives no parameters.

#### Example implementation


```typescript
const googlePayConfig = {
  onKountCollectStart: () => {
    console.log('Kount fraud screening started');
    
    // Track screening start
    trackEvent('kount-screening-started', {
      timestamp: new Date().toISOString()
    });
    
    // Show loading indicator
    showMessage('Verifying transaction...', 'info');
  }
};
```

### onKountCollectEnd

This callback is triggered when Kount fraud screening data collection completes.

You can use it to:

* Track fraud screening completion.
* Hide loading indicators.
* Log screening results.
* Process fraud scores.


#### Event data

This callback receives no parameters.

#### Example implementation


```typescript
const googlePayConfig = {
  onKountCollectEnd: () => {
    console.log('Kount fraud screening completed');
    
    // Track screening completion
    trackEvent('kount-screening-completed', {
      timestamp: new Date().toISOString(),
      duration: getScreeningDuration()
    });
    
    // Hide loading indicator
    hideMessage();
  }
};
```

## Soft decline retry events

### onPreRetrySoftDecline

This callback is triggered before retrying a soft-declined payment with different authentication parameters.

You can use it to:

* Configure retry attempts with alternative challenge indicators.
* Implement custom retry logic for soft declines.
* Track soft decline occurrences.
* Adjust authentication parameters for better success rates.


#### Event data

| Event data | Description |
|  --- | --- |
| `result`BaseSubmitResult | The transaction result containing decline information. |
| `result.merchantTransactionId`string | The merchant transaction ID for tracking. |
| `result.systemTransactionId`string | The system transaction ID for tracking. |
| `result.errorCode`string | The error code indicating the type of decline. |
| `result.errorReason`string | A human-readable description of the decline reason. |
| `result.correlationId`string | The correlation ID for tracking. |


#### Return value

| Return value | Description |
|  --- | --- |
| `boolean` | Return `true` to retry the payment, `false` to cancel. |
| `object` | Return an object with `retry` flag and optional `updatedConfigs` to retry with modified 3DS authentication settings. |
| `object.retry`boolean | Whether to retry the payment. |
| `object.updatedConfigs`object | Updated callback configurations for the retry attempt (e.g., `onPreAuthentication` with different challenge indicator). |


#### Example implementation


```typescript
const googlePayConfig = {
  onPreRetrySoftDecline: (result) => {
    console.log('Soft decline occurred, preparing retry');
    console.log('Merchant Transaction ID:', result.merchantTransactionId);
    console.log('System Transaction ID:', result.systemTransactionId);
    console.log('Error code:', result.errorCode);
    console.log('Decline reason:', result.errorReason);
    console.log('Correlation ID:', result.correlationId);
    
    // Track soft decline
    trackEvent('payment-soft-decline', {
      merchantTransactionId: result.merchantTransactionId,
      systemTransactionId: result.systemTransactionId,
      errorCode: result.errorCode,
      errorReason: result.errorReason,
      correlationId: result.correlationId
    });
    
    // Show message to user
    showMessage('Processing payment with additional verification...', 'info');
    
    // Retry with forced 3DS challenge
    return {
      retry: true,
      updatedConfigs: {
        onPreInitiateAuthentication: () => ({
          providerId: 'pxpfinancial',
          timeout: 12
        }),
        onPreAuthentication: async () => ({
          merchantCountryNumericCode: '826',
          merchantLegalName: 'Your Store Ltd',
          challengeWindowSize: 5,
          requestorChallengeIndicator: '04' // Force challenge on retry
        })
      }
    };
    
    // Or simply return true to retry with same config:
    // return true;
    
    // Or return false to cancel retry:
    // return false;
  }
};
```

## Complete implementation example

Here's a complete example showing how multiple callbacks work together:


```typescript
// Initialise SDK
const pxpSdk = window.PXPCheckout.init({
  merchantGroupName: process.env.REACT_APP_MERCHANT_GROUP,
  merchantName: process.env.REACT_APP_MERCHANT_NAME,
  environment: 'test'
});

// Create consent component
const googlePayConsent = pxpSdk.create('google-pay-consent', {});
googlePayConsent.mount('consent-container');

// Create Google Pay button with comprehensive event handling
const googlePayButton = pxpSdk.create('google-pay-button', {
  paymentDataRequest: {
    allowedPaymentMethods: [{
      type: 'CARD',
      parameters: {
        allowedCardNetworks: ['VISA', 'MASTERCARD', 'AMEX'],
        allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS']
      }
      // Note: tokenizationSpecification is automatically configured by the SDK from session data
    }],
    transactionInfo: {
      currencyCode: 'GBP',
      totalPriceStatus: 'FINAL',
      totalPrice: '25.00'
    },
    emailRequired: true,
    shippingAddressRequired: true,
    callbackIntents: ['SHIPPING_ADDRESS', 'SHIPPING_OPTION']
  },
  
  googlePayConsentComponent: googlePayConsent,
  
  // Pre-authorisation
  onPreAuthorisation: async (data) => {
    const cartTotal = getCurrentCartTotal();
    
    return {
      riskScreeningData: {
        performRiskScreening: true,
        userIp: "192.168.1.100",
        account: {
          id: "user_12345678",
          creationDateTime: "2024-01-15T10:30:00.000Z"
        },
        fulfillments: [{
          type: "Shipped",
          recipientPerson: {
            phoneNumber: "+1234567890"
          }
        }]
      },
      psd2Data: {
        scaExemption: cartTotal < 30 ? 'LowValue' : 'TransactionRiskAnalysis'
      }
    };
  },
  
  // Post-authorisation
  onPostAuthorisation: async (result, paymentData) => {
    if (result && 'merchantTransactionId' in result && !('errorCode' in result)) {
      await updateInventory(result.merchantTransactionId);
      await sendConfirmationEmail({
        transactionId: result.merchantTransactionId,
        email: paymentData.Email
      });
      window.location.href = `/success?txn=${result.merchantTransactionId}`;
    } else if (result && 'errorCode' in result) {
      showError(`Payment declined: ${result.errorReason}`);
    }
  },
  
  // Payment sheet interactions
  onPaymentDataChanged: async (intermediatePaymentData) => {
    const baseAmount = 20.00;
    let tax = 5.00;
    let shippingCost = 0;
    
    if (intermediatePaymentData.shippingAddress) {
      shippingCost = await calculateShipping(intermediatePaymentData.shippingAddress);
      tax = await calculateTax(intermediatePaymentData.shippingAddress, baseAmount);
    }
    
    const newTotal = baseAmount + tax + shippingCost;
    await pxpSdk.updateAmount(newTotal);
    
    return {
      newTransactionInfo: {
        totalPriceStatus: 'FINAL',
        totalPrice: newTotal.toFixed(2),
        displayItems: [
          { label: 'Subtotal', type: 'LINE_ITEM', price: baseAmount.toFixed(2) },
          { label: 'Tax', type: 'TAX', price: tax.toFixed(2) },
          { label: 'Shipping', type: 'LINE_ITEM', price: shippingCost.toFixed(2) }
        ]
      }
    };
  },
  
  // Custom validation
  onCustomValidation: async () => {
    const email = document.getElementById('email')?.value;
    if (!email || !email.includes('@')) {
      showError('Please enter a valid email address');
      return false;
    }
    return true;
  },
  
  // Error handling
  onError: (error) => {
    console.error('Google Pay error:', error);
    logError('google-pay-error', error);
    
    if (!(error.name === 'GooglePayPaymentFailedException' && error.message.includes('cancel'))) {
      showError('Payment failed. Please try again or use a different payment method.');
    }
  },
  
  // Cancellation handling
  onCancel: () => {
    trackEvent('google-pay-cancelled', { cartValue: getCurrentCartValue() });
    saveCartForLater();
  },
  
  // Consent handling
  onGetConsent: () => {
    return document.getElementById('save-payment-checkbox')?.checked || false;
  }
});

// Mount button
googlePayButton.mount('google-pay-container');
```