Skip to content

Instantly share code, notes, and snippets.

@pstadler
Created May 23, 2025 06:39
Show Gist options
  • Save pstadler/f69fe973d05b3fca339e13ff2ddde3d5 to your computer and use it in GitHub Desktop.
Save pstadler/f69fe973d05b3fca339e13ff2ddde3d5 to your computer and use it in GitHub Desktop.

Google Pay Button with Cross-Origin-Embedder-Policy

# Without `Cross-Origin-Embedder-Policy: require-corp` this works correctly
open index.html

# With `require-corp`, the Google Pay button completely fails in Chrome.
# In Safari and Firefox, the button shows an endless loading state,
# but still seems to work at first glance.
docker run -v ./Caddyfile:/etc/caddy/Caddyfile -v ./:/app -p 9000:9000 caddy
open http://localhost:9000/index.html

# Adding the `crossorigin` attribute to the `script` tag breaks with:
# "No 'Access-Control-Allow-Origin' header is present on the requested resource."
open http://localhost:9000/index-crossorigin.html
{
debug
}
:9000 {
file_server browse
root * /app
header Cache-Control "no-cache, no-store, must-revalidate"
header Cross-Origin-Embedder-Policy require-corp
#header Cross-Origin-Resource-Policy cross-origin
}
<!DOCTYPE html>
<html>
<head>
<title>Google Pay COEP / crossorigin</title>
<script src="https://pay.google.com/gp/p/js/pay.js" crossorigin></script>
</head>
<body>
<div id="container"></div>
<script>
const paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' });
const baseRequest = {
apiVersion: 2,
apiVersionMinor: 0
};
const allowedCardAuthMethods = ['PAN_ONLY', 'CRYPTOGRAM_3DS'];
const allowedCardNetworks = ['VISA', 'MASTERCARD'];
const tokenizationSpecification = {
type: 'PAYMENT_GATEWAY',
parameters: {
gateway: 'example',
gatewayMerchantId: 'exampleMerchantId'
}
};
const baseCardPaymentMethod = {
type: 'CARD',
parameters: {
allowedAuthMethods: allowedCardAuthMethods,
allowedCardNetworks: allowedCardNetworks
}
};
const cardPaymentMethod = Object.assign({}, baseCardPaymentMethod, {
tokenizationSpecification: tokenizationSpecification
});
function getGoogleIsReadyToPayRequest() {
return Object.assign({}, baseRequest, {
allowedPaymentMethods: [baseCardPaymentMethod]
});
}
function getGooglePaymentDataRequest() {
return Object.assign({}, baseRequest, {
allowedPaymentMethods: [cardPaymentMethod],
transactionInfo: {
totalPriceStatus: 'FINAL',
totalPrice: '1.00',
currencyCode: 'USD',
countryCode: 'US'
},
merchantInfo: {
merchantId: '12345678901234567890',
merchantName: 'Demo Merchant'
}
});
}
function onGooglePayLoaded() {
paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
.then(function(response) {
if (response.result) {
const button = paymentsClient.createButton({ onClick: onGooglePaymentButtonClicked });
document.getElementById('container').appendChild(button);
} else {
console.error('Google Pay is not available.');
}
})
.catch(function(err) {
console.error('Error determining readiness to use Google Pay: ', err);
});
}
function onGooglePaymentButtonClicked() {
const paymentDataRequest = getGooglePaymentDataRequest();
paymentsClient.loadPaymentData(paymentDataRequest)
.then(function(paymentData) {
console.log('Payment data:', paymentData);
})
.catch(function(err) {
console.error('Payment failed: ', err);
});
}
window.onload = onGooglePayLoaded;
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Google Pay COEP</title>
<script src="https://pay.google.com/gp/p/js/pay.js"></script>
</head>
<body>
<div id="container"></div>
<script>
const paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' });
const baseRequest = {
apiVersion: 2,
apiVersionMinor: 0
};
const allowedCardAuthMethods = ['PAN_ONLY', 'CRYPTOGRAM_3DS'];
const allowedCardNetworks = ['VISA', 'MASTERCARD'];
const tokenizationSpecification = {
type: 'PAYMENT_GATEWAY',
parameters: {
gateway: 'example',
gatewayMerchantId: 'exampleMerchantId'
}
};
const baseCardPaymentMethod = {
type: 'CARD',
parameters: {
allowedAuthMethods: allowedCardAuthMethods,
allowedCardNetworks: allowedCardNetworks
}
};
const cardPaymentMethod = Object.assign({}, baseCardPaymentMethod, {
tokenizationSpecification: tokenizationSpecification
});
function getGoogleIsReadyToPayRequest() {
return Object.assign({}, baseRequest, {
allowedPaymentMethods: [baseCardPaymentMethod]
});
}
function getGooglePaymentDataRequest() {
return Object.assign({}, baseRequest, {
allowedPaymentMethods: [cardPaymentMethod],
transactionInfo: {
totalPriceStatus: 'FINAL',
totalPrice: '1.00',
currencyCode: 'USD',
countryCode: 'US'
},
merchantInfo: {
merchantId: '12345678901234567890',
merchantName: 'Demo Merchant'
}
});
}
function onGooglePayLoaded() {
paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
.then(function(response) {
if (response.result) {
const button = paymentsClient.createButton({ onClick: onGooglePaymentButtonClicked });
document.getElementById('container').appendChild(button);
} else {
console.error('Google Pay is not available.');
}
})
.catch(function(err) {
console.error('Error determining readiness to use Google Pay: ', err);
});
}
function onGooglePaymentButtonClicked() {
const paymentDataRequest = getGooglePaymentDataRequest();
paymentsClient.loadPaymentData(paymentDataRequest)
.then(function(paymentData) {
console.log('Payment data:', paymentData);
})
.catch(function(err) {
console.error('Payment failed: ', err);
});
}
window.onload = onGooglePayLoaded;
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment