Introduction

Google Pay™  Express Checkout lets customers complete a purchase directly from the product, cart, or any pre-checkout page using payment, shipping and contact details already saved in their Google Account — no manual form filling required.

By exposing the Google Pay™ button earlier in the buying journey, merchants can shorten the funnel, reduce abandonment and offer a one-tap experience on the web.

Overview

This page describes only the `Express Checkout` possibility and advices how to handle some specific scenarios. 

Express Checkout builds on top of the standard Google Pay integration. All gateway configuration (gateway: 'payonegmbh', gatewayMerchantId.) remains identical. The extensions described on this page are additive — you layer them onto your existing PaymentDataRequest and existing logic.
Google Pay can collect shipping address, shipping options, billing address, email and apply offers/promo codes directly inside the Google Pay™ payment sheet. 
As you don`t need to have any changes towards Payone API for Google Pay Express - you can use Google Pay documentation directly to implement the Express option. Please be aware that Google Pay Express is not an official name and is not mentioned on Google Pay documentation.

---end

 

Prerequisites

Implementation of the Google pAY  

---end

Please follow our Google Pay integration Guide and complete a standard integration before implementing Express Checkout option.  

---end

---end

 

Express checkout flow

The folowing simplifed steps are done by user/merchant during Google Pay Express.

---end

  1. Customer taps the Google Pay button on a product page, mini-cart, or cart page. The button is rendered with Express Checkout parameters (shipping, offers, email).
  2. Google Pay opens the payment sheet pre-filled with the user's saved cards, addresses, and email. If shippingAddressRequired is true, the sheet shows an address selector.
  3. User selects or changes a shipping address → onPaymentDataChanged fires with callbackTrigger: SHIPPING_ADDRESS.
  4. Your Integration calculates updated shipping costs and returns a PaymentDataRequestUpdate with new transactionInfo and shippingOptionParameters.
  5. User selects a shipping option → onPaymentDataChanged fires with callbackTrigger: SHIPPING_OPTION. Your code recalculates the total and returns an updated transactionInfo.
  6. (Optional) User enters a promo code → onPaymentDataChanged fires with callbackTrigger: OFFER. Your code validates the code, applies the discount and returns the updated totals plus an offerData result.
  7. User confirms payment → onPaymentAuthorized fires. You receive the full paymentData including tokenized card data, billing/shipping addresses, email, and selected shipping option.
  8. Your server sends the payment token to the PAYONE Commerce Platform API exactly as in a standard Google Pay integration. No API-level difference exists for Express Checkout.

---end

---end

 

---end

eNABLING THE EXPRESS CHECKOUT

In order to retrieve address information needed for the express checkout please follow the configuration steps below.  

Configure the callback Intents in the payment data object

---end

Google Pay supports the following callback intents for Express Checkout. Add them to the callbackIntents array in your PaymentDataRequest: 

Intent Triggers callback Purpose Requirement
PAYMENT_AUTHORIZATION  onPaymentAuthorized Invoked after the user authorizes the payment. You must return a PaymentAuthorizationResult to confirm or reject the transaction. Mandatory
SHIPPING_ADDRESS onPaymentDataChanged Fires when the user selects or changes a shipping address. Use it to validate the address and update shipping options / prices. Recommended - (Needed for Express checkout)
SHIPPING_OPTION onPaymentDataChanged Fires when the user picks a different shipping option. Recalculate totals accordingly. Recommended
OFFER onPaymentDataChanged Fires when the user enters or removes a promo/offer code. Validate the code and apply the discount. Optional

The callback method associated with SHIPPING_ADDRESS and SHIPPING_OPTION, onPaymentDataChanged can be used to dynamically update shipping cost.

---end

Callback intents sample
    callbackIntents: [
        'PAYMENT_AUTHORIZATION',
        'SHIPPING_ADDRESS',
        'SHIPPING_OPTION',
        'OFFER',
    ],

---end

Add Shipping configurations to the the payment data object

shippingAddressParameters

Set shippingAddressRequired to request shipping address from the user.

The user can use a saved address or add a new address.

shippingAddressParameters is used to set shipping restrictions. Fields : allowedCountryCodes, phoneNumberRequired, format

---end

shippingAddressParameters sample
                 shippingAddressRequired: true,
                    shippingAddressParameters: {
                        allowedCountryCodes: ['DE', 'US'],
                        phoneNumberRequired: false,
                        format: 'FULL'
                    },

---end

shippingOptionParameters

Set shippingOptionRequired to true to set shippingOptionParameters. It is used to set different shipping options.

shippingOptionParameters is used to define shippinh options Fields- defaultSelectedOptionId and shippingOptions. shippingOptions has the details of different shipping methods - id, label and description

This allows you also to pass a default delivery option

---end

shippingOptionParameters sample
                 shippingOptionRequired: true,
                    shippingOptionParameters: {
                        defaultSelectedOptionId: 'shipping-001',
                        shippingOptions: [
                            {
                                id: 'shipping-001',
                                label: '$0.00: Free shipping',
                                description: 'Free Shipping delivered in 5 business days.'
                            },
                            {
                                id: 'shipping-002',
                                label: '$1.99: Standard shipping',
                                description: 'Standard shipping delivered in 3 business days.'
                            },
                            {
                                id: 'shipping-003',
                                label: '$1000: Express shipping',
                                description: 'Express shipping delivered in 1 business day.'
                            },
                        ],
                    }

---end

Provide the Transaction info

Transaction information has to be configured for the Express checkout flow.  Display Items are mandatory for a use case when you plan to update the Payment after the initiation. We recommend to consult Google Pay documentation for more specifics. 

When Express Checkout includes shipping, set totalPriceStatus: 'ESTIMATED' in your initial transactionInfo. Inside the onPaymentDataChanged callback, once you have computed the definitive total (including shipping and tax), switch to totalPriceStatus: 'FINAL' in the returned PaymentDataRequestUpdate.

Dynamic price updates are the core mechanism of Express Checkout. Every time the user changes their shipping address, selects a different shipping option, or enters a promo code, the onPaymentDataChanged callback fires and you must return updated pricing.

---end

 

---end

Transaction information sample
transactionInfo: {
    totalPriceStatus: "ESTIMATED",
    totalPriceLabel: "Total",
    totalPrice: "908.22",
    currencyCode: "EUR",
  "checkoutOption": "DEFAULT",
    countryCode: "DE",
    displayItems: [
        {
            label: "Pixel 9 Peony 128GB (Unlocked)",
            type: "LINE_ITEM",
            price: "799.00"
        },
        {
            label: "Case-Mate Signature Clear Case for Pixel 9",
            type: "LINE_ITEM",
            price: "29.99"
        },
        {
            label: "Case-Mate Lens Protector for Pixel 9",
            type: "LINE_ITEM",
            price: "19.99"
        },
        {
            label: "Subtotal",
            type: "SUBTOTAL",
            price: "848.98"
        },
        {
            label: "Estimated Tax",
            type: "TAX",
            price: "59.24"
        },
        {
            label: "Shipping",
            type: "LINE_ITEM",
            price: "0.00",
            status: "PENDING"
        }
    ]
}

For more information refer to Transaction Information Google Documentation. 

---end

Handle the callbacks and update the payment information

In order to dynamically update the Final amount and/or implement other business logic it is natural to handle specific Google Pay callbacks below. Most important is is described below. However for complete documentation please consult Response objects  Google Pay Documentation. 

As part of the basic implementation you should already handle onPaymentAuthorized - however this handling now has to be extended to fetch additional information.

---end

Handle onPaymentDataChanged and update the payment information

onPaymentDataChanged callback provides payment data changes in the payment sheet such as shipping address, shipping option, and offer code changes. It is invoked whenever any of the SHIPPING_ADDRESS, SHIPPING_OPTION, or OFFER callback intents are triggered.

You should use the information provided in this callback to modify the Payment request  information .

During the onPaymentDataChanged callback, the shippingAddress is redacted for privacy — you only receive administrativeArea, locality, countryCode, and postalCode. The full address (street, name, etc.) is only available in onPaymentAuthorized.

---end

---end

Sample of an intermediate payload returned from Google Pay API
{
  "callbackTrigger": "SHIPPING_ADDRESS",
  "offerData": {
    "redemptionCode": "exampleCode"
  },
  "shippingAddress": {
    "administrativeArea": "NY",
    "countryCode": "US",
    "locality": "New York",
    "postalCode": "10011"
  },
  "shippingOptionData": {
    "id": "shipping-001"
  }
}

---end

Payment information update handling
Dynamic price updates are the core mechanism of Express Checkout. Every time the user changes their shipping address, selects a different shipping option, or enters a promo code, the onPaymentDataChanged callback fires and you must return updated pricing.

---end

  • Always return newTransactionInfo — even if the price hasn't changed. This ensures the payment sheet stays in sync.
  • Use ESTIMATED on initial load and switch to FINAL once shipping and tax are resolved.
  • Include displayItems to provide transparency. If you show a shipping line item, update its price to match the selected shipping option.
  • The totalPrice must equal the sum of subtotal + shipping + tax − discount. Google does not auto-compute it.
  • Negative prices in displayItems are only allowed for items with type: 'DISCOUNT'.

---end

Sample Implementation for dynamic pricing updates logic
function getShippingCosts() {
    return {
      "shipping-001": "0.00",
      "shipping-002": "1.99",
      "shipping-003": "10.00",
      "shipping-munich-001": "4.99",
      "shipping-munich-002": "14.99",
    };
  }

  function getShippingOptionsForMunich() {
    return {
      defaultSelectedOptionId: "shipping-munich-001",
      shippingOptions: [
        {
          id: "shipping-munich-001",
          label: "$4.99: Standard shipping",
          description: "Standard shipping delivered in 3 business days.",
        },
        {
          id: "shipping-munich-002",
          label: "$14.99: Express shipping",
          description: "Express shipping delivered in 1 business day.",
        },
      ],
    };
  }

  function calculateNewTransactionInfo(shippingOptionId) {
    let newTransactionInfo = transactionInfo;

    // removing the existing shipping info
    newTransactionInfo.displayItems = newTransactionInfo.displayItems.filter(
      (item) => item.type !== "SHIPPING_OPTION",
    );

    let shippingCost = getShippingCosts()[shippingOptionId];
    newTransactionInfo.displayItems.push({
      type: "SHIPPING_OPTION",
      label: "Shipping cost",
      price: shippingCost,
    });

    let totalPrice = 0.0;
    newTransactionInfo.displayItems.forEach(
      (displayItem) => (totalPrice += parseFloat(displayItem.price)),
    );
    newTransactionInfo.totalPrice = totalPrice.toString();

    return newTransactionInfo;
  }

  function onPaymentDataChanged(intermediatePaymentData) {
    let paymentDataRequestUpdate = {};
    let shippingAddress = intermediatePaymentData.shippingAddress;
    let shippingOptionData = intermediatePaymentData.shippingOptionData;

    switch (intermediatePaymentData.callbackTrigger) {
      case "INITIALIZE":
      case "SHIPPING_ADDRESS":
        // Read intermediatePaymentData.transactionInfo
        // Read intermediatePaymentData.shippingAddress
        // Update paymentDataRequestUpdate.newTransactionInfo

        // For example, read the shipping address and update the shipping options
        // if the postal code belongs to Munich
        const postalCode = Number(shippingAddress?.postalCode);
        if (postalCode >= 80331 && postalCode <= 81929) {
          const newShippingOptionParameters = getShippingOptionsForMunich();
          const newTransactionInfo = calculateNewTransactionInfo(newShippingOptionParameters.defaultSelectedOptionId);          );
          paymentDataRequestUpdate.newShippingOptionParameters = newShippingOptionParameters;
          paymentDataRequestUpdate.newTransactionInfo = newTransactionInfo;
        } else if (postalCode === 18581) {
          // throw error is postal code is 18581
          paymentDataRequestUpdate.error = {
            reason: "SHIPPING_ADDRESS_UNSERVICEABLE",
            message: "Cannot ship to the selected address",
            intent: "SHIPPING_ADDRESS",
          };
        }
        break;
      case "SHIPPING_OPTION":
        // Read intermediatePaymentData.transactionInfo
        // Read intermediatePaymentData.shippingOptionData
        // Update paymentDataRequestUpdate.newTransactionInfo
        // Update paymentDataRequestUpdate.newShippingOptionParameters

        const newTransactionInfo = calculateNewTransactionInfo(shippingOptionData.id);
        paymentDataRequestUpdate.newTransactionInfo = newTransactionInfo;
        break;
      default:
    }

    return paymentDataRequestUpdate;
  }

---end

 For complete documentation please consult Google Pay documentation. 

Handle onPaymentAuthorized callback and retrieve Address information

onPaymentAuthorized is Invoked when a payment is authorized in the payment sheet. This should be already handled by your standard implementation. 

For Express checkout flow it is important to extend your handling to fetch additional information based on the configuration provided in the Payment Request/while invoking Google Pay

This is a final callback and the response information (Shipping, Billing, email) should be used to make requests against Payone API.

---end

---end

Sample Response from Google Pay 
{
    "apiVersionMinor": 0,
    "apiVersion": 2,
    "paymentMethodData": {
        "description": "Mastercard •••• 9903",
        "tokenizationData": {
            "type": "PAYMENT_GATEWAY",
            "token": <tokenized-card-data>
        },
        "type": "CARD",
        "info": {
            "cardNetwork": "MASTERCARD",
            "cardDetails": "9903",
            "billingAddress": {
                "phoneNumber": "+49 30 1234567",
                "address3": "",
                "sortingCode": "",
                "address2": "",
                "countryCode": "DE",
                "address1": "Friedrichstraße 43",
                "postalCode": "10117",
                "name": "Max Mustermann",
                "locality": "Berlin",
                "administrativeArea": "BE"
            }
        }
    },
    "shippingOptionData": {
        "id": "shipping-001"
    },
    "shippingAddress": {
        "phoneNumber": "+1 212 5551234",
        "address3": "",
        "sortingCode": "",
        "address2": "Apt 4B",
        "countryCode": "US",
        "address1": "350 Fifth Avenue",
        "postalCode": "10118",
        "name": "John Doe",
        "locality": "New York",
        "administrativeArea": "NY"
    },
    "offerData": {
        "redemptionCodes": []
    },
    "email": "max.mustermann@email.com"
}

---end

 

---end

 

aDDITIONAL FEATURES

Setting the Billing address to be required

---end

Billing address details can be requested from the user by configuring the parameters field under allowedPaymentMethods. These details are collected when the user adds a card to their Google Pay profile.

Parameters can be passed with billingAddressRequired, billingAddressParameters fields to request billing address from the user:

---end

Objecttitle
billingAddressRequired: boolean,
billingAddressParameters: {
    format: 'FULL' | 'FULL-ISO3166' | 'MIN'
    phoneNumberRequired: boolean
}

Example Parameters for the enabled Billing Address information mandate with enforced phone number. 

---end

Sample allowedPaymentMethods object
        allowedPaymentMethods: [
            {
                type: "CARD",
                parameters: {
                    allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
                    allowedCardNetworks: ["MASTERCARD", "VISA"],
                    allowedIssuerCountryCodes: ["DE", "US"],
                    billingAddressRequired: true,
                    billingAddressParameters: {
                        format: "FULL-ISO3166",
                        phoneNumberRequired: true,
                    },
                },
                tokenizationSpecification: {
                    type: "PAYMENT_GATEWAY",
                    parameters: {
                        'gateway': 'payonegmbh',
                        'gatewayMerchantId': 'exampleGatewayMID'
                    },
                },
            },
        ],

---end

 For more information please  refer to documentation from Google regarding BillingAddressParameters

---end

 

---end

COnfiguration and implementation samples

Payment request object with express checkout relevant parameters 

---end

Google Pay Express parameters
paymentData = {
        apiVersion: 2,
        apiVersionMinor: 0,
        allowedPaymentMethods: [
            {
                type: "CARD",
                parameters: {
                    allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
                    allowedCardNetworks: ["MASTERCARD", "VISA"],
                    allowedIssuerCountryCodes: ["DE", "US"],
                    billingAddressRequired: true,
                    billingAddressParameters: {
                        format: "FULL-ISO3166",
                        phoneNumberRequired: true,
                    },
                },
                tokenizationSpecification: {
                    type: "PAYMENT_GATEWAY",
                    parameters: {
                        'gateway': 'payonegmbh',
                        'gatewayMerchantId': 'exampleGatewayMID'
                    },
                },
            },
        ],
        merchantInfo: {
            merchantId: "12345678901234567890",
            merchantName: "Demo Merchant",
        },
        transactionInfo: {
            totalPriceStatus: "FINAL",
            totalPriceLabel: "Total",
            totalPrice: "100.00",
            currencyCode: "EUR",
            countryCode: "DE",
        },
    callbackIntents: [
        'PAYMENT_AUTHORIZATION',
        'SHIPPING_ADDRESS',
        'SHIPPING_OPTION',
        'OFFER',
    ],
    emailRequired: true,
    shippingAddressRequired: true,
    shippingAddressParameters: {
        allowedCountryCodes: ['DE', 'US'],
        phoneNumberRequired: false,
        format: 'FULL'
    },
    shippingOptionRequired: true,
    shippingOptionParameters: {
        defaultSelectedOptionId: 'shipping-001',
        shippingOptions: [
            {
                id: 'shipping-001',
                label: '$0.00: Free shipping',
                description: 'Free Shipping delivered in 5 business days.'
            },
            {
                id: 'shipping-002',
                label: '$1.99: Standard shipping',
                description: 'Standard shipping delivered in 3 business days.'
            },
            {
                id: 'shipping-003',
                label: '$1000: Express shipping',
                description: 'Express shipping delivered in 1 business day.'
            },
        ],
    }
}

---end

 

Response from the Google Pay API

Once the card is authorized and tokenized, the Google Pay API will send the response in the following format:

Webhook
{
    "apiVersionMinor": 0,
    "apiVersion": 2,
    "paymentMethodData": {
        "description": "Mastercard •••• 9903",
        "tokenizationData": {
            "type": "PAYMENT_GATEWAY",
            "token": <tokenized-card-data>
        },
        "type": "CARD",
        "info": {
            "cardNetwork": "MASTERCARD",
            "cardDetails": "9903",
            "billingAddress": {
                "phoneNumber": "+49 30 1234567",
                "address3": "",
                "sortingCode": "",
                "address2": "",
                "countryCode": "DE",
                "address1": "Friedrichstraße 43",
                "postalCode": "10117",
                "name": "Max Mustermann",
                "locality": "Berlin",
                "administrativeArea": "BE"
            }
        }
    },
    "shippingOptionData": {
        "id": "shipping-001"
    },
    "shippingAddress": {
        "phoneNumber": "+1 212 5551234",
        "address3": "",
        "sortingCode": "",
        "address2": "Apt 4B",
        "countryCode": "US",
        "address1": "350 Fifth Avenue",
        "postalCode": "10118",
        "name": "John Doe",
        "locality": "New York",
        "administrativeArea": "NY"
    },
    "offerData": {
        "redemptionCodes": []
    },
    "email": "max.mustermann@email.com"
}

The information provided in the response can now be used to send an API Request to Payone for payment processing. There is no different compared to a `standard` Google Pay implementation. 

Example React Implementation for google pay express checkout button without dynamic updates

Example React implementation
import GooglePayButton from "@google-pay/button-react";

const GooglePay: React.FC = () => {
    return (
        <>
            <GooglePayButton
                className="w-100 mt-2"
                environment="TEST"
                buttonType="pay"
                buttonColor="white"
                buttonLocale="en"
                buttonSizeMode="fill"
                paymentRequest={{
                    apiVersion: 2,
                    apiVersionMinor: 0,
                    allowedPaymentMethods: [
                        {
                            type: "CARD",
                            parameters: {
                                allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
                                allowedCardNetworks: ["MASTERCARD", "VISA"],
                            },
                            tokenizationSpecification: {
                                type: "PAYMENT_GATEWAY",
                                parameters: {
                                    'gateway': 'payonegmbh',
                                    'gatewayMerchantId': 'XSYznIYWmi'
                                },
                            },
                        },
                    ],
                    merchantInfo: {
                        merchantId: "12345678901234567890",
                        merchantName: "Demo Merchant",
                    },
                    transactionInfo: {
                        totalPriceStatus: "FINAL",
                        totalPriceLabel: "Total",
                        totalPrice: "100.00",
                        currencyCode: "EUR",
                        countryCode: "DE",
                    },
                    callbackIntents: [
                        'PAYMENT_AUTHORIZATION',
                        'SHIPPING_ADDRESS',
                        'SHIPPING_OPTION',
                        'OFFER',
                    ],
                    emailRequired: true,
                    shippingAddressRequired: true,
                    shippingAddressParameters: {
                        allowedCountryCodes: ['DE', 'US'],
                        phoneNumberRequired: false,
                        format: 'FULL'
                    },
                    shippingOptionRequired: true,
                    shippingOptionParameters: {
                        defaultSelectedOptionId: 'shipping-001',
                        shippingOptions: [
                            {
                                id: 'shipping-001',
                                label: '$0.00: Free shipping',
                                description: 'Free Shipping delivered in 5 business days.'
                            },
                            {
                                id: 'shipping-002',
                                label: '$1.99: Standard shipping',
                                description: 'Standard shipping delivered in 3 business days.'
                            },
                            {
                                id: 'shipping-003',
                                label: '$1000: Express shipping',
                                description: 'Express shipping delivered in 1 business day.'
                            },
                        ],
                    }
                }}
                onLoadPaymentData={paymentData => console.log(paymentData)}
            />
        </>
    );
}

export default GooglePay;

 

---end