While working on the development of a virtual gift card generator for my application, I wanted to integrate Google Pay as a payment method. The goal was to allow users to pay for virtual gift cards directly on the website. However, during the setup process, I encountered an issue: the Google Pay button was not being displayed on the frontend, and I received the following error in the browser console:
Uncaught ReferenceError: onGooglePayLoaded is not defined
at HTMLScriptElement.onload (index.html:15:38)
In this article, I will walk through how I identified and solved this issue while setting up Google Pay for my application.
The Problem
The error message indicated that the browser could not find the onGooglePayLoaded
function, which is essential for initializing the Google Pay client and adding the payment button to the page. The error occurred because the onGooglePayLoaded
function was not available in the scope when the Google Pay API script was loaded, causing the function to be undefined when the onload
event triggered.
The Google Pay Setup
Initially, I had the following Google Pay setup in my index.html
and client.js
files:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Test Website</h1>
<!-- Google Pay Script -->
<script async
src="https://pay.google.com/gp/p/js/pay.js"
onload="onGooglePayLoaded()">
</script>
<!-- Custom JS for Google Pay setup -->
<script src="client.js"></script>
</body>
</html>
client.js:
const tokenizationSpecification = {
type: 'PAYMENT_GATEWAY',
parameters: {
gateway: 'example',
gatewayMerchantId: 'gatewayMerchantId',
}
};
const cardPaymentMethod = {
type: 'CARD',
tokenizationSpecification: tokenizationSpecification,
parameters: {
allowedCardNetworks: ['VISA', 'MASTERCARD'],
allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
}
};
const googlePayConfiguration = {
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: [cardPaymentMethod]
};
let googlePayClient;
function onGooglePayLoaded() {
googlePayClient = new google.payments.api.PaymentsClient({
environment: 'TEST',
});
googlePayClient.isReadyToPay(googlePayConfiguration)
.then(response => {
if (response.result) {
createAndAddButton();
}
})
.catch(error => console.error('isReadyToPay error: ', error));
}
function createAndAddButton() {
const googlePayButton = googlePayClient.createButton({
onClick: onGooglePayButtonClicked,
});
document.getElementById('buy-now').appendChild(googlePayButton);
}
function onGooglePayButtonClicked() {
const paymentDataRequest = { ...googlePayConfiguration };
paymentDataRequest.merchantInfo = {
merchantId: 'BCR2DN4T767ILP3Z',
merchantName: 'Pepco Virtual Gift Cards',
};
paymentDataRequest.transactionInfo = {
totalPriceStatus: 'FINAL',
totalPrice: '100.00',
currencyCode: 'RSD',
countryCode: 'RS',
};
googlePayClient.loadPaymentData(paymentDataRequest)
.then(paymentData => processPaymentData(paymentData))
.catch(error => console.error('loadPaymentData error:', error));
}
function processPaymentData(paymentData) {
fetch('/process-payment', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(paymentData)
})
.then(response => response.json())
.then(data => console.log('Payment successful:', data))
.catch(error => console.error('Error processing payment:', error));
}
The Root Cause
The issue stemmed from the fact that the onGooglePayLoaded
function was defined in client.js
, but the script was not loaded in the correct order. Specifically, the Google Pay script (pay.js
) was being loaded asynchronously in the HTML file with an inline onload
event:
<script async
src="https://pay.google.com/gp/p/js/pay.js"
onload="onGooglePayLoaded()">
</script>
However, the onGooglePayLoaded
function was defined in client.js
, and there was no guarantee that client.js
would be fully loaded before the Google Pay script. This caused the browser to fail to find onGooglePayLoaded
, as the script was not executed at the right time.
The Solution
The solution was to load the Google Pay script programmatically and attach the onGooglePayLoaded
function to the script’s onload
event. This ensures that the function is executed only after the Google Pay script has been fully loaded.
Here’s the corrected implementation:
Updated client.js
:
// Wait for the DOM to load before defining the function
document.addEventListener('DOMContentLoaded', function () {
const googlePayScript = document.createElement('script');
googlePayScript.src = 'https://pay.google.com/gp/p/js/pay.js';
googlePayScript.async = true;
// Once the script is loaded, initialize Google Pay
googlePayScript.onload = onGooglePayLoaded;
document.body.appendChild(googlePayScript);
});
const tokenizationSpecification = {
type: 'PAYMENT_GATEWAY',
parameters: {
gateway: 'example',
gatewayMerchantId: 'gatewayMerchantId',
}
};
const cardPaymentMethod = {
type: 'CARD',
tokenizationSpecification: tokenizationSpecification,
parameters: {
allowedCardNetworks: ['VISA', 'MASTERCARD'],
allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
}
};
const googlePayConfiguration = {
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: [cardPaymentMethod]
};
let googlePayClient;
function onGooglePayLoaded() {
googlePayClient = new google.payments.api.PaymentsClient({
environment: 'TEST',
});
googlePayClient.isReadyToPay(googlePayConfiguration)
.then(response => {
if (response.result) {
createAndAddButton();
} else {
console.log('Google Pay is not supported on this device or browser.');
}
})
.catch(error => console.error('isReadyToPay error: ', error));
}
function createAndAddButton() {
const googlePayButton = googlePayClient.createButton({
onClick: onGooglePayButtonClicked,
});
document.getElementById('buy-now').appendChild(googlePayButton);
}
function onGooglePayButtonClicked() {
const selectedItem = { price: '100.00' };
const paymentDataRequest = { ...googlePayConfiguration };
paymentDataRequest.merchantInfo = {
merchantId: 'BCR2DN4T767ILP3Z',
merchantName: 'Pepco Virtual Gift Cards',
};
paymentDataRequest.transactionInfo = {
totalPriceStatus: 'FINAL',
totalPrice: selectedItem.price,
currencyCode: 'RSD',
countryCode: 'RS',
};
googlePayClient.loadPaymentData(paymentDataRequest)
.then(paymentData => processPaymentData(paymentData))
.catch(error => console.error('loadPaymentData error:', error));
}
function processPaymentData(paymentData) {
const orderEndpointUrl = '/process-payment';
fetch(orderEndpointUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(paymentData)
})
.then(response => response.json())
.then(data => console.log('Payment successful:', data))
.catch(error => console.error('Error processing payment:', error));
}
Conclusion
By dynamically loading the Google Pay script and ensuring that the onGooglePayLoaded
function was properly linked to the script’s onload
event, the issue was resolved. The Google Pay button now appears correctly on the page, allowing users to make payments through Google Pay. This solution helped me successfully integrate Google Pay into my virtual gift card generator application, which is a key feature of the platform.