---
title: Google Pay
slug: google-pay
createdAt: 2026-11-12T12:00:00.000Z
---

# Overview

Accept Google Pay with Forter in three steps:

1. Configure the Google Pay button to tokenize with the `forter` gateway.
2. Send the encrypted Google Pay token to the Forter Tokenization API. Forter returns Forter tokens.
3. Send the Forter tokens to your PSP through the [Detokenization Proxy](docId\:OfFNU07xBM4EPefHK2jsf).

Forter verifies and decrypts the Google Pay token inside its PCI environment. Your servers never see the card number or the cryptogram.

```mermaid
sequenceDiagram
    participant B as Browser
    participant G as Google Pay
    participant M as Merchant backend
    participant T as Forter Tokenization API
    participant P as Forter Detokenization Proxy
    participant S as PSP
    B->>G: loadPaymentData()
    G-->>B: encrypted payment token
    B->>M: payment token
    M->>T: POST /v1/wallet/tokenize-google-pay
    T-->>M: googlePayDeviceDataToken, paymentCredentialsToken
    M->>P: PSP request with placeholders + token headers
    P->>S: PSP request with card data
    S-->>M: authorization result
```

# Prerequisites

- Forter Tokenization API credentials (`site_id` and `site_secret`). See [Tokenization API](docId\:LV3MxjmewXKauwqXIndlD).
- Detokenization Proxy set up, including Forter's CA certificate. See [Detokenization Proxy](docId\:OfFNU07xBM4EPefHK2jsf).
- A Google Pay integration that follows [Google's Google Pay API documentation](https://developers.google.com/pay/api/web/overview), including the Google Pay & Wallet Console registration that Google requires before you go live.

# Environments

Forter decides which Google environment a token belongs to from your Forter site, not from the token:

| **Google Pay environment** | **Forter site**     | **Tokenization API base URL**                            |
| -------------------------- | ------------------- | -------------------------------------------------------- |
| `TEST`                     | Sandbox (test) site | `https://pci-tokenization-sandbox.checkouttools.com/v1/` |
| `PRODUCTION`               | Production site     | `https://pci.checkouttools.com/v1/`                      |

If the Google environment doesn't match the site type, the token is rejected with `Invalid Google Pay token`.

***

# Step 1: Configure the Google Pay button

Use `PAYMENT_GATEWAY` tokenization with gateway `forter`. Set `gatewayMerchantId` to your Forter `site_id`, the same value you use as your Tokenization API username. If they don't match, Forter rejects the token.

```javascript
const paymentDataRequest = {
  apiVersion: 2,
  apiVersionMinor: 0,
  allowedPaymentMethods: [
    {
      type: "CARD",
      parameters: {
        allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
        allowedCardNetworks: ["VISA", "MASTERCARD", "AMEX", "DISCOVER"],
      },
      tokenizationSpecification: {
        type: "PAYMENT_GATEWAY",
        parameters: {
          gateway: "forter",
          gatewayMerchantId: "<your Forter site_id>",
        },
      },
    },
  ],
  merchantInfo: {
    merchantName: "Example Merchant",
    merchantId: "<your Google merchant ID>", // required by Google in PRODUCTION
  },
  transactionInfo: {
    totalPriceStatus: "FINAL",
    totalPrice: "10.00",
    currencyCode: "USD",
  },
};
```

Forter supports the `CARD` payment method only, with the following auth methods:

| **Auth method**  | **What Google returns**                                                           |
| ---------------- | --------------------------------------------------------------------------------- |
| `CRYPTOGRAM_3DS` | A device-bound token (DPAN) with a cryptogram and, in some cases, an ECI.         |
| `PAN_ONLY`       | The card number saved to the shopper's Google account (FPAN), with no cryptogram. |

***

# Step 2: Tokenize the Google Pay token

:::hint{type="warning"}
**Never call this endpoint from the browser or a mobile app.** Your `site_secret` must stay on your servers. Send the Google Pay token from the browser to your own backend, and call Forter from there.
:::

In the browser, pass `paymentMethodData.tokenizationData.token` to your backend unchanged:

```javascript
const paymentData = await paymentsClient.loadPaymentData(paymentDataRequest);

await fetch("/api/google-pay/token", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ token: paymentData.paymentMethodData.tokenizationData.token }),
});
```

From your backend, call the Tokenization API with HTTP Basic authentication:

```javascript
const res = await fetch("https://pci-tokenization-sandbox.checkouttools.com/v1/wallet/tokenize-google-pay", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Basic " + Buffer.from(`${SITE_ID}:${SITE_SECRET}`).toString("base64"),
  },
  body: JSON.stringify({
    payload: { token: tokenFromBrowser },
  }),
});
const tokens = await res.json();
```

## Request body

| **Field**       | **Type**         | **Required** | **Description**                                                                                                                        |
| --------------- | ---------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `payload.token` | string or object | Yes          | The value of `paymentMethodData.tokenizationData.token`. Send the string as-is (recommended). The parsed JSON object is also accepted. |

## Response

**200: `CRYPTOGRAM_3DS`**

```json
{
  "googlePayDeviceDataToken": "<device data token>",
  "paymentCredentialsToken": "<payment credentials token>"
}
```

**200: `PAN_ONLY`** (no `paymentCredentialsToken`)

```json
{
  "googlePayDeviceDataToken": "<device data token>"
}
```

| **Token**                  | **Contains**                                                           | **Lifetime**                                 |
| -------------------------- | ---------------------------------------------------------------------- | -------------------------------------------- |
| `googlePayDeviceDataToken` | Card number (DPAN or FPAN), expiration date, auth method, card network | Same as a Forter multi-use token             |
| `paymentCredentialsToken`  | Cryptogram and ECI (`CRYPTOGRAM_3DS` only)                             | 24 hours. Use it for a single authorization. |

Tokens are scoped to the site that created them. Use them only with the same site's credentials.

***

# Step 3: Authorize through the Detokenization Proxy

Send your PSP request through the [Detokenization Proxy](docId\:OfFNU07xBM4EPefHK2jsf). Add the Google Pay token headers and put placeholders where your PSP expects the card data. The proxy replaces the placeholders with the decrypted values before it forwards the request to your PSP.

## Headers

| **Header**                                    | **Value**                                                     |
| --------------------------------------------- | ------------------------------------------------------------- |
| `forter-google-pay-device-data-token`         | `googlePayDeviceDataToken`. Always required.                  |
| `forter-google-pay-payment-credentials-token` | `paymentCredentialsToken`. Send it for `CRYPTOGRAM_3DS` only. |

## Placeholders

| **Placeholder**                        | **Description**                                                             |
| -------------------------------------- | --------------------------------------------------------------------------- |
| `{{google_pay_device_account_number}}` | The card number: the DPAN for `CRYPTOGRAM_3DS`, or the FPAN for `PAN_ONLY`. |
| `{{google_pay_cryptogram}}`            | The cryptogram. Requires the payment credentials token.                     |
| `{{google_pay_eci}}`                   | The ECI. Requires the payment credentials token.                            |
| `{{google_pay_expiration_m}}`          | The expiration month as a single-digit number (e.g., 3).                    |
| `{{google_pay_expiration_mm}}`         | The expiration month as a two-digit number (e.g., 03).                      |
| `{{google_pay_expiration_yy}}`         | The expiration year as a two-digit number (e.g., 28).                       |
| `{{google_pay_expiration_yyyy}}`       | The expiration year as a four-digit number (e.g., 2028).                    |
| `{{google_pay_auth_method}}`           | `PAN_ONLY` or `CRYPTOGRAM_3DS`.                                             |
| `{{google_pay_card_network}}`          | The card network reported by Google (e.g., `VISA`).                         |

## Example

The PSP endpoint and body below are illustrative. Use your PSP's API and field names.

:::CodeblockTabs
NodeJS - https-proxy-agent

```javascript
// Trust Forter's proxy CA before the app starts:
//   export NODE_EXTRA_CA_CERTS=[your CA certificate file path]
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const proxyAgent = new HttpsProxyAgent("https://pci-proxy-sandbox.checkouttools.com", {
  headers: {
    "proxy-authorization": `Basic ${Buffer.from(`${SITE_ID}:${SITE_SECRET}`).toString("base64")}`,
    "forter-google-pay-device-data-token": tokens.googlePayDeviceDataToken,
    // CRYPTOGRAM_3DS only
    "forter-google-pay-payment-credentials-token": tokens.paymentCredentialsToken,
  },
});

await axios.post(
  "https://api.example-psp.com/v1/payments",
  {
    orderId: "123456",
    amount: { value: 1000, currency: "USD" },
    card: {
      number: "{{google_pay_device_account_number}}",
      expMonth: "{{google_pay_expiration_mm}}",
      expYear: "{{google_pay_expiration_yyyy}}",
    },
    wallet: {
      type: "GOOGLE_PAY",
      cryptogram: "{{google_pay_cryptogram}}",
      eci: "{{google_pay_eci}}",
    },
  },
  {
    httpsAgent: proxyAgent,
    proxy: false,
    headers: { Authorization: "Bearer <PSP API key>" },
  },
);
```
:::

For `PAN_ONLY`, send only the `forter-google-pay-device-data-token` header and leave out `{{google_pay_cryptogram}}` and `{{google_pay_eci}}`.

***

# Errors

## Tokenization API

The Tokenization API returns HTTP **400** with `{ "message": "..." }` when it rejects a request:

| **Message**                                            | **Cause**                                                                                                                                                                              |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Invalid Google Pay token`                             | Forter couldn't verify or decrypt the token, or the token is malformed. Check that you send `tokenizationData.token` unchanged and that the Google environment matches your site type. |
| `Unsupported Google Pay payment method`                | The payment method isn't `CARD`.                                                                                                                                                       |
| `Unsupported Google Pay auth method`                   | The auth method isn't `PAN_ONLY` or `CRYPTOGRAM_3DS`.                                                                                                                                  |
| `Google Pay token was issued for a different merchant` | `gatewayMerchantId` doesn't match the `site_id` you authenticated with.                                                                                                                |

## Detokenization Proxy

Google Pay errors use the proxy's standard [error status codes](docId\:OfFNU07xBM4EPefHK2jsf).

**556: validation error**

| **Message**                                                                       | **Cause**                                                                                                                                      |
| --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `Google Pay requires device data token`                                           | The payment credentials header was sent without the device data header.                                                                        |
| `Ambiguous token details: Google Pay tokens cannot be combined with other tokens` | Google Pay headers were sent together with other Forter token or alias headers.                                                                |
| `Google Pay cryptogram placeholders require payment credentials token`            | The body uses `{{google_pay_cryptogram}}` or `{{google_pay_eci}}` without the payment credentials header. This always happens with `PAN_ONLY`. |

**555: token not found**

| **Message**                                                                                           | **Cause**                                                                                                    |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `Google Pay device data token is required`                                                            | No device data token header was sent.                                                                        |
| `Google Pay device data token not found`                                                              | The token doesn't exist, belongs to another site, or has expired.                                            |
| `Google Pay payment credentials token not found`                                                      | The token doesn't exist, belongs to another site, or is more than 24 hours old.                              |
| `Google Pay tokens are swapped: device data token and payment credentials token headers are reversed` | The two token headers were swapped, or a payment credentials token was sent alone in the device data header. |

If a token is missing or expired, don't retry. Ask the shopper to pay with Google Pay again and tokenize the new token.
