# Google Pay

> How Google Pay differs across Checkout, Web SDK, and Direct API, website registration before going live, choosing the merchant identifier and decryption mode, the Direct API front-end configuration and transaction flow, the two paths for PAN_ONLY tokens, wallet subscriptions, and common issues.

Google Pay lets customers check out quickly with a card saved to their Google account, in Chrome, Safari, Firefox, Edge, and other major browsers. With Checkout and the Web SDK, Onerway renders the Google Pay button and handles the token. With the Direct API, you load the Google Pay JS SDK yourself, obtain the encrypted payment token, and submit it to Onerway.

## Choose an integration method

| Integration method | Button and token | What you do |
| --- | --- | --- |
| [Checkout](/payments/online-payments/checkout) | The checkout page renders the button; the token never reaches your system | Submit `productType=ALL`, or `lpmsInfo.lpmsType=GooglePay` to lock to Google Pay |
| [Web SDK](/payments/online-payments/sdk) | The SDK renders the button on your page, appearance in [Configure wallet buttons](/payments/online-payments/sdk#configure-wallet-buttons); the token never reaches your system | Receive the result through `payment_result`, see [SDK-owned buttons](/payments/online-payments/sdk#sdk-owned-apple-pay-and-google-pay-buttons); embedded WebView limits in [Google Pay in embedded app WebViews](/payments/online-payments/sdk#google-pay-in-embedded-app-webviews) |
| [Direct API](/payments/online-payments/api) | You load the Google Pay JS SDK and obtain the token | Every step under "Direct API integration" on this page |

All three methods require HTTPS and Google Pay enabled on your Onerway merchant account. Browser and device support follows the [Google Pay Web documentation](https://developers.google.com/pay/api/web); for sandbox testing, join the test card group with a Google account, see [Test cards](/payments/get-started/testing#google-pay-test-cards).

## Setup

Before integrating through the Direct API, decide how token authentication methods are handled, who decrypts the token, and where the merchant identifier comes from.

### Token authentication method

`authMethod` is determined by the status of the customer's card on the Google side and you cannot choose it; Onerway requires `allowedAuthMethods` to declare both values below, accepting only one is not supported:

- `CRYPTOGRAM_3DS`: a card the customer has tokenized on the device; the token carries a dynamic cryptogram and 3DS credentials and can be authorized directly.
- `PAN_ONLY`: a card saved to the Google account but not tokenized on a device; the token carries no 3DS credentials, so the CVC must be collected before it can be charged, see "Two paths for PAN_ONLY tokens".

Both values occur in real traffic, and not declaring `PAN_ONLY` means you cannot charge those customers. `PAN_ONLY` does not mean the card number reaches you: with Onerway decryption the token is encrypted with the Onerway key and the card number never enters your systems, and on the standard path the CVC is collected on an Onerway-hosted page, so no PCI DSS compliance is required. PCI DSS applies only if you choose merchant decryption or collect the CVC yourself.

### Decryption mode

| Mode | Front-end tokenization | Submitted to Onerway | Prerequisite |
| --- | --- | --- | --- |
| **Onerway decryption** (recommended) | `PAYMENT_GATEWAY`, with the gateway parameters from List available payment methods | `tokenInfo`, token passed through unchanged | None |
| **Merchant decryption** | `DIRECT`, `ECv2` with the public key registered in your own Google Pay & Wallet Console | Omit `tokenInfo`; put the decrypted result in `cardInfo` | PCI DSS required |

For merchant decryption, map the decrypted result into [`cardInfo`](/payments/api-reference/endpoints/direct-create-transaction#request-cardInfo) as follows:

| Decrypted Google field | `cardInfo` field |
| --- | --- |
| `pan` | `cardNumber` |
| `expirationMonth`, `expirationYear` | `month`, `year` |
| `authMethod` | `wallet.googlePay.authMethod` |
| `cryptogram`, `eciIndicator` (`CRYPTOGRAM_3DS` only) | `cryptogram`, `eci` |

### Merchant identifier

Google requires `merchantInfo.merchantId` when `environment` is `PRODUCTION` (optional in `TEST`). Its value depends on how your domain is registered; choose one:

- Onerway registers your domain under its Google Pay & Wallet Console profile: use the `merchantId` returned by List available payment methods.
- You register your own Google Pay & Wallet Console profile and domain: use your own merchant identifier. Merchant decryption requires this option.

Confirm with Onerway technical support which account you will use, then complete [website registration before going live](#website-registration-before-going-live).

## Website registration before going live

This applies only to **Direct API** integrations; Checkout and the Web SDK use Onerway's Google Pay integration and need no website registration.

Register your website and obtain Google's approval before accepting production payments; a successful sandbox test does not replace it. Follow Google's [Publish your integration](https://developers.google.com/pay/api/web/guides/test-and-deploy/publish-your-integration) guide for the Google Pay account you use:

| Google Pay account | Who submits the website for approval | What you need to do |
| --- | --- | --- |
| Your own account | You | Add the website in your Google Pay & Wallet Console with its domain and integration screenshots, then submit it for approval. |
| Onerway's account | Onerway | Send the domain and the five screenshots below to Onerway technical support; Onerway submits the website and notifies you of the result. |

The domain is the website that calls the Google Pay API. The account determines only the [`merchantInfo.merchantId`](#merchant-identifier) to use: your own account does not require merchant decryption, and when Onerway decrypts the token, `gatewayName` and `gatewayMerchantId` still come from [List available payment methods](/payments/api-reference/endpoints/list-available-payment-methods).

### Five screenshots for Onerway registration

Provide one screenshot for each stage of your purchase flow:

| Screenshot | What it must show |
| --- | --- |
| Item selection | The customer browsing an item or service. |
| Pre-purchase screen | The customer ready to make a purchase. |
| Payment method screen | The customer selecting Google Pay as the payment method. |
| Google Pay API payment screen | The Google Pay payment sheet showing the customer's saved payment information. |
| Post-purchase screen | The page shown after a successful purchase. |

If Android blocks screenshots of the Google Pay payment sheet, photograph the screen with another device; for this item only, an image of an error message is also accepted.

### Switch to production after approval

After Google approves the website, set the [`merchantInfo.merchantId`](#merchant-identifier) of the registering account, create the `PaymentsClient` with `environment=PRODUCTION`, and create transactions with your Onerway production API base URL and credentials.

## Direct API integration

<steps level="3">

### Fetch the Onerway configuration

On your server, call [List available payment methods](/payments/api-reference/endpoints/list-available-payment-methods), take the `paymentMethod=GooglePay` record, and return the configuration to the page. Cache it and refresh it when it changes.

| Returned field | Google Pay parameter |
| --- | --- |
| `gatewayName` | `gateway` in `tokenizationSpecification` |
| `gatewayMerchantId` | `gatewayMerchantId` in `tokenizationSpecification` |
| `subCardTypes` | `allowedCardNetworks`, already upper case as Google expects; pass through unchanged |
| `merchantId` | `merchantInfo.merchantId` (see "Merchant identifier") |
| `countryCode` | `transactionInfo.countryCode` |

### Initialize and show the button

Load the Google Pay JS SDK, create a `PaymentsClient`, check availability with `isReadyToPay()`, and render the official button with `createButton()` once it passes.

```html
<script async src="https://pay.google.com/gp/p/js/pay.js" onload="onGooglePayLoaded()"></script>
```

```js
const paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' }) // 'PRODUCTION' when live
```

### Start the payment

On click, build the `PaymentDataRequest`, call `loadPaymentData()`, and take the token from `tokenizationData.token` in the result to your server. The request needs at least `apiVersion: 2`, `allowedPaymentMethods` with the tokenization parameters, `transactionInfo` with `totalPriceStatus` as `FINAL` plus `totalPrice`, `currencyCode`, and `countryCode`, and `merchantInfo`. Enable `emailRequired` or `shippingAddressRequired` if you want the Google Pay sheet to collect contact details.

### Create the transaction

On your server, call [Create direct transaction](/payments/api-reference/endpoints/direct-create-transaction) with `productType=CARD`, `subProductType=DIRECT`, and `txnType=SALE`, submitting `tokenInfo` or `cardInfo` according to the decryption mode; complete requests are shown in the "Onerway-decrypted Google Pay payment" and "Merchant-decrypted Google Pay CRYPTOGRAM_3DS payment" examples of that endpoint.

### Handle the synchronous response

A `respCode` other than `20000` is a failure; `data.status=S` is success; `data.status=R` with `actionType=RedirectURL` means the token is `PAN_ONLY` and the CVC is still needed: redirect the customer to `redirectUrl` (an Onerway-hosted page) immediately, and the customer returns to `txnOrderMsg.returnUrl` after entering it. In that response `transactionId` is `null`, so associate the order by `merchantTxnId`. Any other status is processing; wait for the webhook.

### Confirm the final result

The final state is determined by the [payment result webhook](/payments/api-reference/webhooks/payment-result), which carries `walletTypeName=GooglePay` for wallet transactions; check that the amount and currency in the webhook match your order. Signature verification, acknowledgement, and deduplication are covered in [Webhooks](/payments/get-started/webhooks).

</steps>

## Two paths for PAN_ONLY tokens

| Aspect | Standard path | You collect the CVC |
| --- | --- | --- |
| CVC collection | Onerway-hosted page | Your own input field |
| Customer experience | One redirect and return | No redirect |
| Implementation | Handle the `status=R` redirect only | One extra check call plus a CVC input |
| CVC security responsibility | Onerway | You: never store or log it, transmit over HTTPS, clear it after use |

Neither path needs extra enablement.

When you collect the CVC yourself:

- After obtaining the token, call [Check Google Pay PAN_ONLY token](/payments/api-reference/endpoints/check-google-pay-pan-only) first.
- When `checkResult=false`, collect the CVC from the customer and create the direct transaction with the same `tokenInfo` and the CVC in `cardInfo.cvv`. For a `PAN_ONLY` token submitted without `cardInfo.cvv`, the transaction response contains `data.status=R` and `data.redirectUrl`; redirect the customer to `data.redirectUrl` to enter the CVC on the Onerway-hosted page. This applies whether or not you called the check endpoint before creating the transaction.
- When `checkResult=true`, create the direct transaction with the same `tokenInfo`; `cardInfo` is not required.
- The check and the transaction must use the same token and the same `merchantTxnId`; fall back to the standard path if the check call fails.
- Validate the CVC length in real time (3 digits for most card networks, 4 for American Express), display it as a password field, and tell the customer why it is needed.

## Front-end example

Fill in the configuration from your server; `processPayment` hands the token to your server.

```html
<div id="container"></div>
<script>
  // Configuration from the GooglePay record returned by List available payment methods on your server
  const onerwayConfig = {
    gateway: '<gatewayName>',
    gatewayMerchantId: '<gatewayMerchantId>',
    allowedCardNetworks: ['MASTERCARD', 'VISA'],
    merchantId: '<merchantId>',
    countryCode: 'US'
  }

  const baseRequest = { apiVersion: 2, apiVersionMinor: 0 }
  const baseCardPaymentMethod = {
    type: 'CARD',
    parameters: { allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], allowedCardNetworks: onerwayConfig.allowedCardNetworks }
  }
  const cardPaymentMethod = {
    ...baseCardPaymentMethod,
    tokenizationSpecification: {
      type: 'PAYMENT_GATEWAY',
      parameters: { gateway: onerwayConfig.gateway, gatewayMerchantId: onerwayConfig.gatewayMerchantId }
    }
  }

  let paymentsClient = null
  function getPaymentsClient() {
    if (!paymentsClient) {
      paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' }) // 'PRODUCTION' when live
    }
    return paymentsClient
  }

  function getPaymentDataRequest() {
    return {
      ...baseRequest,
      allowedPaymentMethods: [cardPaymentMethod],
      transactionInfo: { countryCode: onerwayConfig.countryCode, currencyCode: 'USD', totalPriceStatus: 'FINAL', totalPrice: '99.99' },
      merchantInfo: { merchantId: onerwayConfig.merchantId, merchantName: 'Example Store' }
    }
  }

  function onGooglePayLoaded() {
    getPaymentsClient()
      .isReadyToPay({ ...baseRequest, allowedPaymentMethods: [baseCardPaymentMethod] })
      .then((res) => {
        if (!res.result) return
        const button = getPaymentsClient().createButton({ onClick: onButtonClicked, allowedPaymentMethods: [baseCardPaymentMethod] })
        document.getElementById('container').appendChild(button)
      })
  }

  function onButtonClicked() {
    getPaymentsClient()
      .loadPaymentData(getPaymentDataRequest())
      .then(processPayment)
      .catch(() => {
        // The customer closed the Google Pay sheet or the payment failed; restore the page
      })
  }

  async function processPayment(paymentData) {
    // Never log or store the token
    const paymentToken = paymentData.paymentMethodData.tokenizationData.token
    // Server calls Create direct transaction (tokenInfo.provider=GooglePay), maps a respCode other than 20000 to status 'F', and returns { status, redirectUrl }
    const res = await fetch('/api/google-pay/process-payment', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ paymentToken })
    }).then(r => r.json())

    if (res.status === 'R' && res.redirectUrl) {
      window.location.href = res.redirectUrl // PAN_ONLY: redirect to the Onerway-hosted page to collect the CVC; the final state comes from the webhook
    } else if (res.status === 'S') {
      // Synchronous success; the payment result webhook remains the final source of truth
    } else if (res.status === 'F') {
      // Failed; ask the customer to choose another payment method
    } else {
      // Processing; wait for the payment result webhook
    }
  }
</script>
<script async src="https://pay.google.com/gp/p/js/pay.js" onload="onGooglePayLoaded()"></script>
```

Google resources: [Web integration guide](https://developers.google.com/pay/api/web), [interactive demos](https://developers.google.com/pay/api/web/guides/resources/demos), [brand guidelines](https://developers.google.com/pay/api/web/guides/brand-guidelines).

## Wallet subscriptions

Google Pay can be used for subscriptions: submit `subProductType=SUBSCRIBE` with `subscription` on the initial subscription; both `selfExecute=1` (managed by Onerway) and `selfExecute=2` (you initiate each billing) are supported. On Checkout, lock to Google Pay with `lpmsInfo.lpmsType=GooglePay`; on the Direct API, submit the encrypted wallet token in `tokenInfo` to create the initial subscription. After the initial subscription succeeds, the [subscription payment webhook](/payments/api-reference/webhooks/subscription-payment) returns `contractId` and the subscription `tokenId`; renewals and plan changes then work the same as card subscriptions, see [Subscription payments](/payments/online-payments/scenarios/subscriptions).

Differences from card subscriptions: the encrypted wallet token is single-use and cannot be saved for reuse, so the only billing credential during the subscription is the subscription token; wallet subscriptions produce no saved payment method result webhook and return no card token.

## Common issues

| Symptom | Common causes | What to do |
| --- | --- | --- |
| The button does not appear | `pay.js` not loaded, not HTTPS, no usable card in the account, or an embedded app WebView that does not meet the requirements | Check the `isReadyToPay()` result; for WebViews see [Web SDK integration](/payments/online-payments/sdk#google-pay-in-embedded-app-webviews) |
| Configuration cannot be fetched | Your server did not return `gatewayName`, `gatewayMerchantId`, or `subCardTypes` | Check the List available payment methods call and filtering |
| The production sheet reports an unverified merchant | `merchantInfo.merchantId` missing or not matching the domain registration approach | Confirm the source of the value under "Merchant identifier" |
| PAN_ONLY not handled | Create direct transaction returned `redirectUrl` but the page did not redirect | Redirect as soon as `redirectUrl` is returned, or switch to collecting the CVC yourself |

## Go-live checklist

- Direct API only: [website registration](#website-registration-before-going-live) is complete and approved by Google.
- The token is never decrypted on the client and never logged or stored.
- Both the `PAN_ONLY` redirect path and the `CRYPTOGRAM_3DS` direct success path have been tested with real cards.
- If you collect the CVC yourself, the fallback for a failed check call has been tested.
- Webhook signature verification, deduplication, and amount checks are in place.
