Dynamic checkout updates
Update totals, shipping options, and offers in real time while the Google Pay payment sheet is open.
Google Pay's paymentDataCallbacks let you recalculate the order total, shipping options, and line items while the payment sheet is open — before the customer authenticates. This applies to direct API integrations. Elements and hosted checkout handle supported recalculations automatically.
Callback contract. Every callback must resolve — return a result or throw — before the customer can proceed. A callback that never returns freezes the payment sheet indefinitely. Add timeouts to any network calls inside callbacks and always return a result, even on error.
Set up callbacksAsk
Declare paymentDataCallbacks in the PaymentsClient constructor and add callbackIntents to your paymentDataRequest. Every intent you declare must have a corresponding handler — declaring an intent without a handler causes the sheet to stall.
const googlePayClient = new google.payments.api.PaymentsClient({
environment: 'PRODUCTION',
paymentDataCallbacks: {
onPaymentDataChanged: onPaymentDataChanged,
},
});
const paymentDataRequest = {
apiVersion: 2,
apiVersionMinor: 0,
callbackIntents: ['SHIPPING_ADDRESS', 'SHIPPING_OPTION'],
shippingAddressRequired: true,
shippingAddressParameters: {
phoneNumberRequired: false,
},
shippingOptionRequired: true,
// ...allowedPaymentMethods, merchantInfo, transactionInfo
};Shipping address changeAsk
onPaymentDataChanged fires with callbackTrigger: 'SHIPPING_ADDRESS' when the customer selects or changes their address. Google provides a redacted address — postal code, administrative area, locality, and country only. Use this to compute shipping cost and tax without the full address.
async function onPaymentDataChanged(intermediatePaymentData) {
const { callbackTrigger, shippingAddress } = intermediatePaymentData;
if (callbackTrigger === 'INITIALIZE' || callbackTrigger === 'SHIPPING_ADDRESS') {
const shippingOptions = await fetchShippingOptions(shippingAddress);
if (shippingOptions.length === 0) {
return {
error: {
reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
message: "We don't deliver to this address.",
intent: 'SHIPPING_ADDRESS',
},
};
}
return {
newShippingOptionParameters: {
defaultSelectedOptionId: shippingOptions[0].id,
shippingOptions,
},
newTransactionInfo: calculateTotal(shippingOptions[0]),
};
}
return {};
}The INITIALIZE trigger fires when the sheet first opens. Handle it the same as SHIPPING_ADDRESS to pre-populate shipping options immediately.
Shipping option changeAsk
callbackTrigger: 'SHIPPING_OPTION' fires when the customer switches between shipping tiers. Return an updated transactionInfo reflecting the new cost.
if (callbackTrigger === 'SHIPPING_OPTION') {
const { shippingOptionData } = intermediatePaymentData;
return {
newTransactionInfo: calculateTotal(shippingOptionData),
};
}Returning errors to the sheetAsk
Return an error object to display an inline message and block the customer from proceeding with an unserviceable selection.
return {
error: {
reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
message: "We don't deliver to this country.",
intent: 'SHIPPING_ADDRESS',
},
};| Error reason | When to use |
|---|---|
SHIPPING_ADDRESS_UNSERVICEABLE | Address is outside your delivery area |
SHIPPING_OPTION_INVALID | Selected shipping option is no longer available |
OFFER_INVALID | Applied offer code is invalid or expired |
PAYMENT_DATA_INVALID | Generic — use only when no more specific reason applies |
transactionInfo format referenceAsk
Return updated transactionInfo in any callback that changes the total. displayItems are optional but recommended — they appear as an itemized breakdown in the sheet.
const transactionInfo = {
totalPriceStatus: 'FINAL', // 'FINAL' | 'ESTIMATED'
totalPrice: '45.00', // string, decimal format
currencyCode: 'EUR',
countryCode: 'BG',
displayItems: [
{ label: 'Subtotal', type: 'SUBTOTAL', price: '38.00' },
{ label: 'Shipping', type: 'LINE_ITEM', price: '5.00' },
{ label: 'Tax', type: 'TAX', price: '2.00' },
],
};Use totalPriceStatus: 'ESTIMATED' while shipping or tax is not yet resolved — for example, before the customer selects an address. Switch to 'FINAL' once all amounts are confirmed. The totalPriceStatus at the time the customer authenticates determines what is charged.
See alsoAsk
Set up Google Pay (API)
The base integration that dynamic updates build on.
Go-live checklist
Callback compliance is a go-live checklist item.
Google Pay overview
Integration paths and credential type overview.
Last updated on