Learn how to diagnose and fix common issues with the Google Pay web component.
The Google Pay web component throws specific exceptions for different error scenarios:
| Exception | Error Code | Description |
|---|---|---|
GooglePayLoadFailedException | SDK0704 | The Google Pay API failed to load. For example, due to network issues, blocked resources, or CSP restrictions. |
GooglePayNotReadyException | SDK0701 | Google Pay isn't ready on this device or browser. For example, unsupported browser, no Google account, or no payment methods configured. |
GooglePayClientNotInitializedException | SDK0703 | The Google Pay client was not properly initialised before attempting a payment. |
GooglePayConfigurationValidationFailedException | SDK0705 | Configuration validation failed. For example, missing required fields, invalid payment method configuration, or incorrect format. |
GooglePayPaymentFailedException | SDK0702 | Payment processing failed. For example, due to network issues, declined payment, or server errors. |
GooglePayDecryptAndTokenVaultFailedException | SDK0706 | Payment token decryption or vault storage failed. Check gateway configuration and token handling. |
UnsupportedFundingTypeGooglePaySdkException | SDK0110 | Google Pay is not included in the allowed funding types for this session. |
Payment cancellation by the user is not thrown as an exception. Instead, use the onCancel callback to handle user cancellations gracefully.
const config = {
onError: (error) => {
// Log error for debugging
console.error('Google Pay error:', {
name: error.name,
message: error.message,
code: error.ErrorCode, // Note: Capital 'E' in ErrorCode
stack: error.stack,
timestamp: new Date().toISOString()
});
// Handle specific error types by exception name
if (error.name === 'GooglePayNotReadyException') {
showUserMessage('Google Pay is not available on this device. Please use an alternative payment method.');
showAlternativePaymentMethods();
} else if (error.name === 'GooglePayConfigurationValidationFailedException') {
showUserMessage('Payment system temporarily unavailable. Please try again in a few moments.');
logCriticalError('Configuration validation error', error);
} else if (error.name === 'GooglePayClientNotInitializedException') {
showUserMessage('Payment system not ready. Please refresh and try again.');
logCriticalError('Client not initialized', error);
} else if (error.name === 'GooglePayLoadFailedException') {
showUserMessage('Unable to load payment system. Please check your connection and try again.');
offerRetryOption();
} else if (error.name === 'GooglePayPaymentFailedException') {
showUserMessage('Payment failed. Please check your payment information and try again.');
offerRetryOption();
} else if (error.name === 'GooglePayDecryptAndTokenVaultFailedException') {
showUserMessage('Payment processing error. Please try again.');
logCriticalError('Token vault error', error);
} else {
showUserMessage('An unexpected error occurred. Please try again or contact support.');
logUnknownError(error);
}
},
// Handle user cancellation separately (not an exception)
onCancel: () => {
console.log('User cancelled Google Pay');
trackEvent('google_pay_cancelled');
}
};
function showUserMessage(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = message;
document.getElementById('payment-container').appendChild(errorDiv);
// Auto-dismiss after 5 seconds
setTimeout(() => {
errorDiv.remove();
}, 5000);
}
function logCriticalError(context, error) {
// Send to error monitoring service
if (window.errorReporting) {
window.errorReporting.captureException(error, { context });
}
}The symptoms of this are:
- The button element is hidden or not rendered.
- There's an empty container where the button should appear.
- No Google Pay option is visible to customers.
// Step 1: Check Google Pay availability
console.log('Checking Google Pay availability...');
// Check if Google Pay API is loaded
if (!window.google || !window.google.payments || !window.google.payments.api) {
console.error('Google Pay API not loaded');
console.log('Ensure the Google Pay script is included and loaded');
return;
}
// Step 2: Check browser compatibility
const browserInfo = {
userAgent: navigator.userAgent,
isChrome: /Chrome\/(\d+)/.test(navigator.userAgent),
isSafari: /Safari\//.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent),
isFirefox: /Firefox\/(\d+)/.test(navigator.userAgent)
};
console.log('Browser info:', browserInfo);
// Step 3: Check HTTPS
if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
console.error('Google Pay requires HTTPS in production');
return;
}
// Step 4: Verify payment method configuration
const paymentMethod = config.paymentDataRequest.allowedPaymentMethods[0];
if (!paymentMethod || !paymentMethod.parameters) {
console.error('Payment method configuration is missing');
return;
}
console.log('Allowed card networks:', paymentMethod.parameters.allowedCardNetworks);
console.log('Allowed auth methods:', paymentMethod.parameters.allowedAuthMethods);
// Note: The SDK automatically configures tokenizationSpecification from the session
// Step 5: Test Google Pay readiness
try {
const paymentsClient = new google.payments.api.PaymentsClient({
environment: 'TEST'
});
const isReadyToPayRequest = {
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: config.paymentDataRequest.allowedPaymentMethods
};
paymentsClient.isReadyToPay(isReadyToPayRequest)
.then(response => {
if (response.result) {
console.log('✅ Google Pay is ready');
} else {
console.warn('⚠️ Google Pay is not ready - user may not have payment methods configured');
}
})
.catch(error => {
console.error('❌ Google Pay readiness check failed:', error);
});
} catch (error) {
console.error('Failed to check Google Pay readiness:', error);
}
console.log('All diagnostic checks complete');// Solution 1: Graceful fallback
function initializeGooglePay() {
if (checkGooglePayAvailability()) {
renderGooglePayButton();
} else {
console.log('Google Pay not available, showing alternatives');
renderAlternativePaymentMethods();
}
}
function checkGooglePayAvailability() {
return window.google &&
window.google.payments &&
window.google.payments.api &&
isHttps() &&
hasValidPaymentMethods();
}
function isHttps() {
return location.protocol === 'https:' || location.hostname === 'localhost';
}
function hasValidPaymentMethods() {
const paymentMethods = config.paymentDataRequest?.allowedPaymentMethods;
return paymentMethods &&
paymentMethods.length > 0 &&
paymentMethods[0].parameters?.allowedCardNetworks?.length > 0;
}
function renderAlternativePaymentMethods() {
const container = document.getElementById('payment-buttons');
container.innerHTML = `
<button class="credit-card-button">Pay with Credit Card</button>
<button class="paypal-button">Pay with PayPal</button>
`;
}
// Solution 2: Progressive loading
function loadGooglePayWithFallback() {
const timeout = setTimeout(() => {
console.warn('Google Pay API loading timeout');
renderAlternativePaymentMethods();
}, 5000); // 5 second timeout
// Load Google Pay API
const script = document.createElement('script');
script.src = 'https://pay.google.com/gp/p/js/pay.js';
script.async = true;
script.onload = () => {
clearTimeout(timeout);
console.log('Google Pay API loaded successfully');
initializeGooglePay();
};
script.onerror = () => {
clearTimeout(timeout);
console.error('Failed to load Google Pay API');
renderAlternativePaymentMethods();
};
document.head.appendChild(script);
}The symptoms of this are:
- Button is visible and clickable but nothing happens.
- No error messages are shown.
- Payment sheet doesn't appear after button click.
// Enhanced button click handler with diagnostics
const googlePayConfig = {
onGooglePaymentButtonClicked: async (event) => {
console.group('🔍 Google Pay Button Click Diagnostics');
// Check 1: Verify button was actually clicked
console.log('Button clicked:', event);
// Check 2: Verify configuration
console.log('Payment request config:', googlePayConfig.paymentDataRequest);
// Check 3: Verify Google Pay client
if (!window.google || !window.google.payments || !window.google.payments.api) {
console.error('❌ Google Pay API not available');
console.groupEnd();
return;
}
// Check 4: Test payment data request
try {
const paymentsClient = new google.payments.api.PaymentsClient({
environment: pxpSdk.environment === 'live' ? 'PRODUCTION' : 'TEST'
});
// Validate payment data request
console.log('Testing payment data request...');
const testRequest = {
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: googlePayConfig.paymentDataRequest.allowedPaymentMethods
};
const readyToPay = await paymentsClient.isReadyToPay(testRequest);
console.log('Ready to pay:', readyToPay);
if (!readyToPay.result) {
console.error('❌ Google Pay not ready - no payment methods configured');
}
} catch (error) {
console.error('❌ Payment request test failed:', error);
}
console.groupEnd();
}
};// Solution 1: Add validation before opening sheet
const googlePayConfig = {
onCustomValidation: async () => {
console.log('Running custom validation...');
const validations = [
{
name: 'Email',
test: () => {
const email = document.getElementById('email')?.value;
return email && email.includes('@');
},
message: 'Please enter a valid email address'
},
{
name: 'Terms',
test: () => document.getElementById('terms')?.checked,
message: 'Please accept terms and conditions'
},
{
name: 'Amount',
test: () => {
const amount = parseFloat(googlePayConfig.paymentDataRequest.transactionInfo.totalPrice);
return amount > 0;
},
message: 'Invalid payment amount'
}
];
for (const validation of validations) {
if (!validation.test()) {
console.error(`Validation failed: ${validation.name}`);
showError(validation.message);
return false;
}
console.log(`✅ ${validation.name} validation passed`);
}
return true;
}
};
// Solution 2: Handle configuration errors
function createGooglePayButton() {
try {
// Validate configuration before creating button
validateGooglePayConfiguration(googlePayConfig);
const googlePayButton = pxpSdk.create('google-pay-button', googlePayConfig);
googlePayButton.mount('google-pay-container');
console.log('✅ Google Pay button created successfully');
} catch (error) {
console.error('❌ Failed to create Google Pay button:', error);
if (error.name === 'GooglePayConfigurationValidationFailedException') {
showError('Payment configuration error. Please contact support.');
logConfigurationError(error);
} else {
showError('Unable to initialise Google Pay. Please try again.');
}
// Show alternative payment methods
showAlternativePaymentMethods();
}
}
function validateGooglePayConfiguration(config) {
if (!config.paymentDataRequest) {
throw new Error('paymentDataRequest is required');
}
if (!config.paymentDataRequest.transactionInfo) {
throw new Error('transactionInfo is required');
}
if (!config.paymentDataRequest.allowedPaymentMethods ||
config.paymentDataRequest.allowedPaymentMethods.length === 0) {
throw new Error('allowedPaymentMethods is required');
}
const amount = parseFloat(config.paymentDataRequest.transactionInfo.totalPrice);
if (isNaN(amount) || amount <= 0) {
throw new Error('Invalid transaction amount');
}
}The symptoms of this are:
- The Google Pay sheet appears but payment fails.
- Customer sees a generic error message.
- Transaction doesn't complete successfully.
// Enhanced authorisation handler with detailed logging
const googlePayConfig = {
onPreAuthorisation: async (data) => {
console.group('💳 Payment Authorisation Diagnostics');
console.log('Pre-authorisation data:', {
hasData: !!data,
billingAddress: data?.billingAddress ? 'Present' : 'Missing',
timestamp: new Date().toISOString()
});
try {
// Validate amount
const amount = getOrderTotal();
console.log('Transaction amount:', amount);
if (amount <= 0) {
console.error('❌ Invalid amount:', amount);
throw new Error('Invalid transaction amount');
}
// Check inventory
console.log('Checking inventory...');
const inventoryAvailable = await checkInventory();
console.log('Inventory available:', inventoryAvailable);
if (!inventoryAvailable) {
console.error('❌ Inventory not available');
throw new Error('Items no longer available');
}
console.log('✅ Pre-authorisation checks passed');
console.groupEnd();
return {
riskScreeningData: {
performRiskScreening: true
}
};
} catch (error) {
console.error('❌ Pre-authorisation failed:', error);
console.groupEnd();
return null; // Cancel payment
}
},
onPostAuthorisation: async (result, paymentData) => {
console.group('📊 Payment Authorisation Result');
const isSuccess = 'merchantTransactionId' in result;
console.log('Result type:', isSuccess ? 'Success (MerchantSubmitResult)' : 'Failure (FailedSubmitResult)');
console.log('Transaction IDs:', {
merchantTransactionId: result.merchantTransactionId,
systemTransactionId: result.systemTransactionId
});
if (!isSuccess && 'errorCode' in result) {
console.error('Payment not authorized:', {
errorCode: result.errorCode,
errorReason: result.errorReason,
correlationId: result.correlationId
});
} else if (isSuccess) {
console.log('✅ Payment authorized successfully');
}
console.groupEnd();
// Handle result...
}
};// Solution 1: Enhanced validation
function validatePaymentData(paymentData) {
const validations = [
{
check: () => paymentData && Object.keys(paymentData).length > 0,
error: 'Payment data is missing'
},
{
check: () => {
const amount = getOrderTotal();
return amount > 0 && amount < 1000000;
},
error: 'Invalid payment amount'
},
{
check: () => getCurrentCustomerEmail(),
error: 'Customer email is required'
}
];
for (const validation of validations) {
if (!validation.check()) {
throw new Error(validation.error);
}
}
}
// Solution 2: Implement retry logic
async function processPaymentWithRetry(paymentData, maxRetries = 2) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`Payment attempt ${attempt} of ${maxRetries}`);
const result = await processPayment(paymentData);
console.log('Payment processed successfully');
return result;
} catch (error) {
console.warn(`Payment attempt ${attempt} failed:`, error.message);
if (isRetryableError(error) && attempt < maxRetries) {
console.log(`Retrying payment...`);
await delay(1000 * attempt); // Progressive delay
continue;
}
console.error('Payment failed after all retries');
throw error;
}
}
}
function isRetryableError(error) {
const retryableErrors = [
'network error',
'timeout',
'temporary failure',
'service unavailable'
];
return retryableErrors.some(retryableError =>
error.message.toLowerCase().includes(retryableError)
);
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}The symptoms of this are:
- Customer selects shipping address but sees error.
- Unable to calculate shipping costs.
- Address validation failures.
const googlePayConfig = {
onPaymentDataChanged: async (intermediatePaymentData) => {
console.group('📍 Shipping Address Diagnostics');
const address = intermediatePaymentData.shippingAddress;
console.log('Shipping address:', address);
if (!address) {
console.warn('⚠️ No shipping address provided');
console.groupEnd();
return {};
}
// Validate address fields
const requiredFields = ['countryCode', 'postalCode', 'administrativeArea'];
const missingFields = requiredFields.filter(field => !address[field]);
if (missingFields.length > 0) {
console.error('❌ Missing address fields:', missingFields);
} else {
console.log('✅ All required address fields present');
}
// Test shipping calculation
try {
console.log('Testing shipping calculation...');
const shippingCost = await calculateShippingCost(address);
console.log('Shipping cost calculated:', shippingCost);
} catch (error) {
console.error('❌ Shipping calculation failed:', error);
}
console.groupEnd();
// Return actual response...
}
};const googlePayConfig = {
onPaymentDataChanged: async (intermediatePaymentData) => {
try {
const address = intermediatePaymentData.shippingAddress;
if (!address) {
return {};
}
// Validate address completeness
if (!address.countryCode || !address.postalCode) {
return {
error: {
reason: 'SHIPPING_ADDRESS_INVALID',
message: 'Please enter a complete address',
intent: 'SHIPPING_ADDRESS'
}
};
}
// Check if we ship to this country
const allowedCountries = ['US', 'CA', 'GB', 'FR', 'DE'];
if (!allowedCountries.includes(address.countryCode)) {
return {
error: {
reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
message: `We do not ship to ${address.countryCode}`,
intent: 'SHIPPING_ADDRESS'
}
};
}
// Calculate shipping with error handling
let shippingCost = 0;
try {
shippingCost = await calculateShippingCost(address);
} catch (error) {
console.error('Shipping calculation failed:', error);
return {
error: {
reason: 'SHIPPING_ADDRESS_INVALID',
message: 'Unable to calculate shipping. Please try again.',
intent: 'SHIPPING_ADDRESS'
}
};
}
const baseAmount = getBaseAmount();
const tax = calculateTax(address, baseAmount);
const newTotal = baseAmount + tax + shippingCost;
// Update SDK amount
pxpSdk.updateAmount(newTotal);
return {
newTransactionInfo: {
totalPriceStatus: 'FINAL',
totalPrice: newTotal.toFixed(2),
displayItems: [
{ label: 'Subtotal', type: 'LINE_ITEM', price: baseAmount.toFixed(2) },
{ label: 'Shipping', type: 'LINE_ITEM', price: shippingCost.toFixed(2) },
{ label: 'Tax', type: 'TAX', price: tax.toFixed(2) }
]
}
};
} catch (error) {
console.error('Payment data changed error:', error);
return {
error: {
reason: 'SHIPPING_ADDRESS_INVALID',
message: 'An error occurred. Please try again.',
intent: 'SHIPPING_ADDRESS'
}
};
}
}
};To get more detailed information about errors, use this comprehensive debugging setup:
// Enable comprehensive logging
window.googlePayDebug = {
logLevel: 'verbose',
logPaymentData: false, // Never log actual payment tokens
logNetworkRequests: true
};
// Debug helper functions
const debugGooglePay = {
checkEnvironment() {
console.group('🔍 Google Pay Environment Check');
console.log('Protocol:', location.protocol);
console.log('Hostname:', location.hostname);
console.log('User Agent:', navigator.userAgent);
console.log('Google Pay API Available:', !!(window.google && window.google.payments));
console.log('Environment:', pxpSdk.environment);
console.groupEnd();
},
logPaymentRequest(request) {
console.group('📋 Payment Request Configuration');
console.log('Allowed Payment Methods:', request.allowedPaymentMethods);
console.log('Transaction Info:', request.transactionInfo);
console.log('Merchant Info:', request.merchantInfo);
console.log('Callback Intents:', request.callbackIntents);
console.groupEnd();
},
logConfiguration(config) {
console.group('⚙️ Google Pay Configuration');
const paymentMethod = config.paymentDataRequest.allowedPaymentMethods[0];
console.log('Allowed Card Networks:', paymentMethod?.parameters?.allowedCardNetworks);
console.log('Auth Methods:', paymentMethod?.parameters?.allowedAuthMethods);
console.log('Billing Address Required:', paymentMethod?.parameters?.billingAddressRequired);
console.log('Total Price:', config.paymentDataRequest.transactionInfo?.totalPrice);
console.log('Currency:', config.paymentDataRequest.transactionInfo?.currencyCode);
console.log('Country Code:', config.paymentDataRequest.transactionInfo?.countryCode);
// Note: tokenizationSpecification is configured automatically by the SDK
console.groupEnd();
},
testReadiness: async function(config) {
console.group('🧪 Google Pay Readiness Test');
try {
const paymentsClient = new google.payments.api.PaymentsClient({
environment: pxpSdk.environment === 'live' ? 'PRODUCTION' : 'TEST'
});
const isReadyToPayRequest = {
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: config.paymentDataRequest.allowedPaymentMethods
};
const response = await paymentsClient.isReadyToPay(isReadyToPayRequest);
if (response.result) {
console.log('✅ Google Pay is ready to accept payments');
console.log('Payment methods available:', response.paymentMethodPresent);
} else {
console.warn('⚠️ Google Pay not ready - user may need to add payment methods');
}
} catch (error) {
console.error('❌ Readiness test failed:', error);
}
console.groupEnd();
}
};
// Use in your implementation
debugGooglePay.checkEnvironment();
debugGooglePay.logConfiguration(googlePayConfig);
await debugGooglePay.testReadiness(googlePayConfig);// Pre-flight checks before initialising Google Pay
async function performPreflightChecks() {
const checks = [
{
name: 'HTTPS check',
test: () => location.protocol === 'https:' || location.hostname === 'localhost',
fix: 'Ensure your site is served over HTTPS'
},
{
name: 'Google Pay API loaded',
test: () => !!(window.google && window.google.payments && window.google.payments.api),
fix: 'Ensure Google Pay API script is loaded'
},
{
name: 'Payment methods configured',
test: () => {
const pm = googlePayConfig.paymentDataRequest?.allowedPaymentMethods?.[0];
return pm?.parameters?.allowedCardNetworks?.length > 0;
},
fix: 'Configure at least one payment method with allowed card networks'
},
{
name: 'Valid transaction amount',
test: () => {
const amount = parseFloat(googlePayConfig.paymentDataRequest.transactionInfo.totalPrice);
return !isNaN(amount) && amount > 0;
},
fix: 'Ensure transaction amount is valid'
},
{
name: 'Browser compatibility',
test: () => checkBrowserCompatibility(),
fix: 'Use a supported browser'
}
];
const results = [];
for (const check of checks) {
const passed = await check.test();
results.push({ name: check.name, passed, fix: check.fix });
if (!passed) {
console.warn(`❌ ${check.name}: ${check.fix}`);
} else {
console.log(`✅ ${check.name}`);
}
}
return results.every(result => result.passed);
}
function checkBrowserCompatibility() {
const ua = navigator.userAgent;
// Chrome 61+
if (/Chrome\/(\d+)/.test(ua)) {
return parseInt(RegExp.$1) >= 61;
}
// Safari 12.1+
if (/Version\/(\d+)/.test(ua) && /Safari/.test(ua)) {
return parseInt(RegExp.$1) >= 12;
}
// Firefox 62+
if (/Firefox\/(\d+)/.test(ua)) {
return parseInt(RegExp.$1) >= 62;
}
return false;
}
// Use before initialization
async function initializeGooglePay() {
const preflightPassed = await performPreflightChecks();
if (preflightPassed) {
const googlePayButton = pxpSdk.create('google-pay-button', googlePayConfig);
googlePayButton.mount('google-pay-container');
} else {
showFallbackPaymentOptions();
}
}// Error monitoring setup
const errorMonitoring = {
captureError(error, context) {
// Send to your error tracking service
const errorData = {
message: error.message,
name: error.name,
errorCode: error.ErrorCode,
stack: error.stack,
context: context,
userAgent: navigator.userAgent,
url: location.href,
timestamp: new Date().toISOString(),
userId: getCurrentUserId(),
sessionId: getSessionId()
};
// Example: Send to Sentry, LogRocket, etc.
this.sendToErrorService(errorData);
},
trackGooglePayEvent(eventType, data) {
// Track Google Pay specific events
const eventData = {
type: eventType,
data: data,
timestamp: new Date().toISOString(),
sessionId: getSessionId()
};
this.sendToAnalytics(eventData);
}
};
// Integration with Google Pay component
const config = {
onError: (error) => {
errorMonitoring.captureError(error, { component: 'GooglePay' });
},
onPostAuthorisation: (result, paymentData) => {
const isSuccess = 'merchantTransactionId' in result;
errorMonitoring.trackGooglePayEvent('payment_completed', {
success: isSuccess,
transactionId: result.merchantTransactionId
});
}
};