Process Google Pay transactions without 3D Secure authentication for faster, streamlined checkout.
By implementing a non-3DS Google Pay payment flow, you benefit from:
- Faster checkout: Streamlined payment process with fewer steps and no authentication redirects.
- Better conversion: Reduced friction leads to higher completion rates.
- Simplified integration: Fewer callbacks and less complex implementation.
- Lower latency: Direct payment processing without authentication steps.
- Native security: Benefits from Google Pay's tokenisation and biometric authentication.
However, non-3DS payments may not qualify for liability shift protection and are best suited for low-risk transactions, trusted customers, or regions where Strong Customer Authentication (SCA) isn't mandated.
Google Pay provides inherent security through device-based authentication (fingerprint, face recognition, or PIN) and payment tokenisation, making non-3DS flows suitable for many transaction types.
The non-3DS Google Pay payment flow consists of five streamlined steps:
The customer clicks the Google Pay button, triggering the payment sheet to open. Google Pay handles device authentication (biometric or PIN) internally.
Within the Google Pay payment sheet, the customer selects their preferred payment method. Google Pay tokenises the payment data using its secure tokenisation system.
The customer confirms the payment in the Google Pay sheet. The SDK receives the encrypted payment token from Google Pay.
The SDK evaluates whether 3DS authentication is required. In non-3DS flows, authentication is skipped based on:
- Configuration not requiring 3DS (no
onPreInitiateAuthenticationcallback provided). - Transaction falling below risk thresholds.
- Regulatory exemptions being applicable.
- Merchant risk assessment.
Since 3DS isn't required, the flow proceeds directly to authorisation.
The SDK sends the authorisation request with the Google Pay token directly to the payment gateway without 3DS authentication data.
onPreAuthorisation: Provides transaction data for final review before authorisation. This is your last opportunity to add additional data or cancel the transaction.onPostAuthorisation: Receives the final transaction result. The transaction is either authorised or declined.
To use non-3DS Google Pay payments:
- Ensure your merchant account supports non-3DS transactions.
- Configure Google Pay in Unity Portal with your gateway merchant ID.
- Whitelist your domain in Unity Portal.
- Consider implementing additional fraud prevention measures.
Initialise the PXP SDK with your merchant credentials.
import { PxpCheckout } from '@pxpio/web-components-sdk';
const pxpSdk = PxpCheckout.initialize({
environment: 'test', // or 'live' for production
session: sessionData, // Get from your backend
ownerId: 'your-owner-id',
ownerType: 'MerchantGroup',
kountDisabled: false, // OPTIONAL: Set to true to disable Kount fraud detection
transactionData: {
currency: 'GBP',
amount: 25.00,
merchantTransactionId: crypto.randomUUID(),
merchantTransactionDate: () => new Date().toISOString(),
entryType: 'Ecom',
intent: {
card: 'Authorisation'
}
}
});Configure the Google Pay button with the required callbacks for non-3DS payments.
const googlePayButton = pxpSdk.create('google-pay-button', {
paymentDataRequest: {
allowedPaymentMethods: [{
type: 'CARD',
parameters: {
allowedCardNetworks: ['VISA', 'MASTERCARD', 'AMEX'],
allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'] // Both methods supported
}
// Note: tokenizationSpecification is automatically configured by the SDK from session data
}],
transactionInfo: {
currencyCode: 'GBP',
totalPriceStatus: 'FINAL',
totalPrice: '49.99',
displayItems: [
{
label: 'Subtotal',
type: 'LINE_ITEM',
price: '45.00'
},
{
label: 'VAT',
type: 'TAX',
price: '4.99'
}
]
}
},
// REQUIRED: Final transaction review before authorisation
onPreAuthorisation: async (data) => {
console.log('Processing non-3DS Google Pay payment');
console.log('Gateway Token ID:', data.gatewayTokenId);
// Add any additional data
return {
riskScreeningData: {
performRiskScreening: true,
excludeDeviceData: false
}
};
},
// REQUIRED: Handle the final result
onPostAuthorisation: (result, paymentData) => {
console.log('Payment result:', result);
if (result && 'merchantTransactionId' in result) {
// Success - MerchantSubmitResult
console.log('Payment successful!');
console.log('Transaction ID:', result.merchantTransactionId);
console.log('System Transaction ID:', result.systemTransactionId);
// 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);
showError('Payment declined. Please try another payment method.');
}
},
// OPTIONAL: Handle errors
onError: (error) => {
console.error('Payment error:', error);
showError('An error occurred. Please try again.');
},
// OPTIONAL: Handle cancellation
onCancel: () => {
console.log('Payment cancelled by user');
showMessage('Payment cancelled');
}
});Apply different risk measures based on transaction amounts.
const googlePayButton = pxpSdk.create('google-pay-button', {
// ... basic configuration
onPreAuthorisation: async (data) => {
console.log('Gateway Token ID:', data.gatewayTokenId);
// Get transaction amount from your application state
const amount = getTransactionAmount();
// Different handling based on amount
if (amount < 30) {
// Low-value transaction - minimal checks
return {
riskScreeningData: {
performRiskScreening: false,
excludeDeviceData: true
}
};
} else if (amount < 100) {
// Medium-value transaction - standard checks
return {
riskScreeningData: {
performRiskScreening: true,
excludeDeviceData: false
}
};
} else {
// High-value transaction - enhanced checks
return {
riskScreeningData: {
performRiskScreening: true,
excludeDeviceData: false,
items: [{
price: amount,
quantity: 1,
category: 'high-value'
}]
}
};
}
}
});Handle different customer types with varying risk profiles.
function getCustomerRiskProfile(customerId) {
const customer = getCustomerDetails(customerId);
if (!customer.previousOrders) {
return { level: 'high', verification: 'enhanced', reason: 'new-customer' };
}
if (customer.previousOrders > 10 && customer.chargebacks === 0) {
return { level: 'low', verification: 'minimal', reason: 'trusted-customer' };
}
return { level: 'medium', verification: 'standard', reason: 'returning-customer' };
}
const googlePayButton = pxpSdk.create('google-pay-button', {
// ... basic configuration
onPreAuthorisation: async (data) => {
const customerId = getCurrentCustomerId();
const riskProfile = getCustomerRiskProfile(customerId);
console.log('Customer risk profile:', riskProfile);
return {
riskScreeningData: {
performRiskScreening: riskProfile.level !== 'low',
excludeDeviceData: false,
transaction: {
subtotal: amount
}
}
};
}
});Apply different rules based on customer location.
const googlePayButton = pxpSdk.create('google-pay-button', {
// ... basic configuration
onPreAuthorisation: async (data) => {
console.log('Gateway Token ID:', data.gatewayTokenId);
const ipAddress = await getCustomerIPAddress();
const geolocation = await getIPGeolocation(ipAddress);
// Check if customer is in high-risk region
const highRiskCountries = ['XX', 'YY', 'ZZ'];
const isHighRisk = highRiskCountries.includes(geolocation.countryCode);
return {
riskScreeningData: {
performRiskScreening: true,
excludeDeviceData: false
}
};
}
});Implement comprehensive error handling for non-3DS payments.
const googlePayButton = pxpSdk.create('google-pay-button', {
// ... basic configuration
onPostAuthorisation: (result, paymentData) => {
if (result && 'merchantTransactionId' in result) {
// Payment successful - MerchantSubmitResult
handlePaymentSuccess(result, paymentData);
} else if (result && 'errorCode' in result) {
// Payment declined or failed - FailedSubmitResult
handlePaymentFailure(result);
}
},
onError: (error) => {
console.error('Payment error:', error);
// Handle specific error types
if (error.code === 'BUYER_ACCOUNT_ERROR') {
showError('There was an issue with your Google Pay account. Please try another payment method.');
} else if (error.code === 'MERCHANT_CONFIGURATION_ERROR') {
showError('Payment system configuration error. Please contact support.');
logError('Google Pay merchant configuration error', error);
} else if (error.code === 'NETWORK_ERROR') {
showError('Connection failed. Please check your internet and try again.');
} else {
showError('Payment failed. Please try again or use a different payment method.');
}
},
onCancel: () => {
console.log('Payment cancelled by user');
// Customer closed Google Pay sheet
showMessage('Payment cancelled. Your order is still in your basket.');
}
});
function handlePaymentSuccess(result, paymentData) {
console.log('✅ Payment successful');
console.log('Transaction ID:', result.merchantTransactionId);
console.log('System Transaction ID:', result.systemTransactionId);
// Store transaction details
storeTransactionRecord({
transactionId: result.merchantTransactionId,
systemTransactionId: result.systemTransactionId,
processingType: 'non-3ds',
paymentMethod: 'google-pay',
timestamp: new Date().toISOString()
});
// Redirect to success page
window.location.href = '/payment-success?txn=' + result.merchantTransactionId;
}
function handlePaymentFailure(result) {
console.error('❌ Payment failed');
console.error('Error code:', result.errorCode);
console.error('Error reason:', result.errorReason);
console.error('Correlation ID:', result.correlationId);
// Map error codes to user-friendly messages
const errorMessages = {
'INSUFFICIENT_FUNDS': 'Insufficient funds. Please try a different payment method.',
'CARD_DECLINED': 'Payment declined by your bank. Please try another card.',
'EXPIRED_CARD': 'This card has expired. Please use a different payment method.',
'INVALID_CARD': 'Invalid card details. Please try another payment method.',
'FRAUD_DETECTED': 'Payment could not be processed. Please contact your bank.',
'PROCESSING_ERROR': 'Payment processing error. Please try again.',
'LIMIT_EXCEEDED': 'Transaction exceeds your card limit. Try a smaller amount.',
'DO_NOT_HONOR': 'Payment declined by your bank. Please try a different payment method.'
};
const userMessage = errorMessages[result.errorCode] ||
'Payment could not be completed. Please try a different payment method.';
showError(userMessage);
// Log for analysis
logPaymentDecline({
errorCode: result.errorCode,
errorReason: result.errorReason,
correlationId: result.correlationId,
httpStatusCode: result.httpStatusCode,
timestamp: new Date().toISOString()
});
// Offer alternative payment methods
showAlternativePaymentOptions();
}Mount the Google Pay button to your page.
googlePayButton.mount('google-pay-container');The following example shows a complete non-3DS Google Pay implementation.
import { useEffect, useState } from 'react';
function GooglePayCheckout() {
const [googlePayButton, setGooglePayButton] = useState(null);
const [isProcessing, setIsProcessing] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
// Initialise SDK
const pxpSdk = PxpCheckout.initialize({
environment: 'test',
session: sessionData, // Get from your backend
ownerId: 'your-owner-id',
ownerType: 'MerchantGroup',
kountDisabled: false, // OPTIONAL: Set to true to disable Kount fraud detection
transactionData: {
currency: 'GBP',
amount: 25.00,
merchantTransactionId: crypto.randomUUID(),
merchantTransactionDate: () => new Date().toISOString(),
entryType: 'Ecom',
intent: {
card: 'Authorisation'
}
}
});
// Create Google Pay button (non-3DS)
const button = 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',
countryCode: 'GB',
totalPriceStatus: 'FINAL',
totalPrice: '49.99',
totalPriceLabel: 'Total',
displayItems: [
{
label: 'Premium Headphones',
type: 'LINE_ITEM',
price: '45.00',
status: 'FINAL'
},
{
label: 'VAT (20%)',
type: 'TAX',
price: '4.99',
status: 'FINAL'
}
]
}
},
// Handle button click
onGooglePaymentButtonClicked: () => {
console.log('Google Pay button clicked');
setIsProcessing(true);
setError(null);
},
// Pre-authorisation - add risk data
onPreAuthorisation: async (data) => {
console.log('🔐 Processing non-3DS payment');
console.log('Gateway Token ID:', data.gatewayTokenId);
// Assess risk
const customerId = getCurrentCustomerId();
const riskProfile = await assessCustomerRisk(customerId);
return {
riskScreeningData: {
performRiskScreening: true,
excludeDeviceData: false
}
};
},
// Post-authorisation - handle result
onPostAuthorisation: (result, paymentData) => {
console.log('📊 Payment result received');
setIsProcessing(false);
if (result && 'merchantTransactionId' in result) {
// Success - MerchantSubmitResult
console.log('✅ Payment authorised');
// Track successful payment
trackPaymentSuccess({
transactionId: result.merchantTransactionId,
systemTransactionId: result.systemTransactionId,
paymentMethod: 'google-pay'
});
// Redirect to success page
window.location.href = '/order-confirmation?txn=' + result.merchantTransactionId;
} else if (result && 'errorCode' in result) {
// Failure - FailedSubmitResult
console.error('❌ Payment declined:', result.errorReason);
setError('Payment declined. Please try another payment method.');
// Track decline
trackPaymentDecline({
errorCode: result.errorCode,
errorReason: result.errorReason,
correlationId: result.correlationId
});
}
},
// Error handling
onError: (error) => {
console.error('❌ Payment error:', error);
setIsProcessing(false);
setError('An error occurred. Please try again.');
// Track error
trackPaymentError({
errorCode: error.code,
errorMessage: error.message
});
},
// Cancellation handling
onCancel: () => {
console.log('Payment cancelled by user');
setIsProcessing(false);
setError(null);
// Track cancellation
trackPaymentCancellation();
}
});
// Mount button
button.mount('google-pay-button-container');
setGooglePayButton(button);
// Cleanup
return () => {
if (button) {
button.unmount();
}
};
}, []);
return (
<div className="checkout-container">
<div className="order-summary">
<h2>Order summary</h2>
<div className="order-items">
<div className="order-item">
<span>Premium Headphones</span>
<span>£45.00</span>
</div>
<div className="order-item">
<span>VAT (20%)</span>
<span>£4.99</span>
</div>
<div className="order-total">
<span>Total</span>
<span>£49.99</span>
</div>
</div>
</div>
<div className="payment-section">
<h2>Pay with Google Pay</h2>
{error && (
<div className="error-message">
{error}
</div>
)}
<div id="google-pay-button-container"></div>
{isProcessing && (
<div className="processing-indicator">
Processing payment...
</div>
)}
<div className="payment-info">
<p>✓ Fast and secure checkout with Google Pay</p>
<p>✓ No need to enter card details</p>
<p>✓ Protected by Google Pay's security</p>
</div>
</div>
</div>
);
}
// Helper functions
function getCurrentCustomerId() {
return localStorage.getItem('customerId') || 'guest';
}
async function assessCustomerRisk(customerId) {
// Implement your risk assessment logic
const orderHistory = await getCustomerOrderHistory(customerId);
if (orderHistory.totalOrders > 10 && orderHistory.chargebacks === 0) {
return 'low';
} else if (orderHistory.totalOrders === 0) {
return 'high';
}
return 'medium';
}
function trackPaymentSuccess(data) {
console.log('Tracking payment success:', data);
// Implement your analytics tracking
}
function trackPaymentDecline(data) {
console.log('Tracking payment decline:', data);
// Implement your analytics tracking
}
function trackPaymentError(data) {
console.log('Tracking payment error:', data);
// Implement your analytics tracking
}
function trackPaymentCancellation() {
console.log('Tracking payment cancellation');
// Implement your analytics tracking
}
export default GooglePayCheckout;This section describes the data received by the different callbacks as part of the non-3DS Google Pay flow.
The onPreAuthorisation callback receives only the gateway token ID. Use this ID to retrieve token details and make authorisation decisions on your backend.
| Parameter | Description |
|---|---|
dataobject | Object containing token information. |
data.gatewayTokenIdstring | The token ID from the payment gateway. Use this ID to retrieve full token details from Unity backend and update transaction decision. |
Use the gatewayTokenId with the Get masked card data related to gateway token API to retrieve full token details including card scheme, funding source (credit/debit), masked PAN, and expiry date.
onPreAuthorisation: async (data) => {
console.log('Non-3DS Google Pay authorisation for token:', data.gatewayTokenId);
// Retrieve token details and make authorisation decision
const transactionDecision = await getAuthorisationDecision(data.gatewayTokenId);
if (!transactionDecision) {
// Not proceeding with authorisation
console.log('Not proceeding with authorization');
return null;
}
// Add risk screening data
return {
riskScreeningData: {
performRiskScreening: true,
excludeDeviceData: false
}
};
}Important: 3DS external data should no longer be provided via the callback. For non-3DS flows, no 3DS data is applicable.
The onPostAuthorisation callback receives the authorisation result and the original payment data from Google Pay.
| Parameter | Description |
|---|---|
resultMerchantSubmitResult or FailedSubmitResult | Object containing the transaction result. Can be either a success result (MerchantSubmitResult) or failure result (FailedSubmitResult). |
result.merchantTransactionIdstring | (Success only) The merchant's unique identifier for the transaction. |
result.systemTransactionIdstring | (Success only) The system's unique identifier for the transaction. |
result.errorCodestring | (Failure only) The error code indicating the type of failure. |
result.errorReasonstring | (Failure only) The human-readable error message. |
result.correlationIdstring | (Failure only) The correlation ID for tracking and debugging. |
result.httpStatusCodenumber | (Failure only) The HTTP status code of the failure. |
paymentDataobject | The original payment data from Google Pay, including payment method details and billing information. |
onPostAuthorisation: (result, paymentData) => {
console.log('Non-3DS Google Pay payment result');
if (result && 'merchantTransactionId' in result) {
// Success - MerchantSubmitResult
console.log('✅ Payment successful!');
console.log('Merchant Transaction ID:', result.merchantTransactionId);
console.log('System Transaction ID:', result.systemTransactionId);
// Store transaction details
storeTransactionRecord({
merchantTransactionId: result.merchantTransactionId,
systemTransactionId: result.systemTransactionId,
paymentMethod: 'google-pay',
processingType: 'non-3ds',
timestamp: new Date().toISOString()
});
// Redirect to success page
window.location.href = `/payment-success?txn=${result.merchantTransactionId}`;
} else if (result && 'errorCode' in result) {
// Failure - FailedSubmitResult
console.error('❌ Payment failed');
console.error('Error code:', result.errorCode);
console.error('Error reason:', result.errorReason);
console.error('Correlation ID:', result.correlationId);
// Handle specific error types
handlePaymentFailure(result);
}
}When the payment is successful, you'll receive a MerchantSubmitResult:
{
merchantTransactionId: "order-123456",
systemTransactionId: "sys-txn-789012"
}| Parameter | Description |
|---|---|
merchantTransactionIdstring | The merchant's unique identifier for the transaction that was provided during SDK initialisation. |
systemTransactionIdstring | The system's unique identifier for the transaction. Use this with merchantTransactionId to retrieve full authorisation details from Unity backend. |
When the payment fails, you'll receive a FailedSubmitResult:
{
errorCode: "CARD_DECLINED",
errorReason: "Payment declined by issuing bank",
correlationId: "corr_abc123def456",
httpStatusCode: 402
}| Parameter | Description |
|---|---|
errorCodestring | The error code indicating the type of failure. |
errorReasonstring | A human-readable description of the error. |
correlationIdstring | A unique identifier for this transaction attempt. Use this for support inquiries and debugging. |
httpStatusCodenumber | The HTTP status code associated with the failure. |
The onError callback is triggered when an error occurs during the Google Pay flow (before reaching authorisation).
| Parameter | Description |
|---|---|
errorobject | Object containing error information. |
error.codestring | The error code indicating the type of error. |
error.messagestring | A human-readable description of the error. |
error.statusCodestring or undefined | Google Pay API status code, if available. |
onError: (error) => {
console.error('Google Pay error:', error);
// Re-enable payment form
hideLoadingSpinner();
enablePaymentOptions();
}The onCancel callback is triggered when the customer closes the Google Pay payment sheet without completing the payment.
This callback does not receive any parameters.
onCancel: () => {
console.log('Google Pay payment cancelled by user');
// Track cancellation for analytics
trackEvent('payment-cancelled', {
paymentMethod: 'google-pay',
timestamp: new Date().toISOString()
});
// Show message to user
showMessage('Payment cancelled. Your order is still in your basket.');
// Reset UI state
hideLoadingSpinner();
enablePaymentOptions();
}