> For the complete documentation index, see [llms.txt](https://plexo.gitbook.io/rest-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://plexo.gitbook.io/rest-api/wallets/google-pay/google-pay-api.md).

# API Integration

Apple Pay and Google Pay availability comes from current Visa/Mastercard routing on every channel. See [ownership and payment-method eligibility](/rest-api/api-reference/wallet-integrations.md#wallet-ownership-and-availability) before following this guide. Use only eligible methods in `allowedCardNetworks` or `supportedNetworks`; examples listing both assume both are eligible. With `managedBy: "handy"`, Handy owns configuration and native provider setup; request changes through Handy. References to your provider credentials mean the resolved owner's credentials. Your team still validates its app/device, and the original Plexo merchant remains the recipient and Google `gatewayMerchantId`.

Use this flow when you want to render the Google Pay™ button in your own UI and send the resulting token to Plexo through the Payments API.

In this model:

* Your frontend renders the Google Pay button
* Your backend or BFF fetches runtime Google Pay configuration from Plexo
* Your frontend collects the Google Pay token
* Your frontend sends the encrypted payment data to your backend or BFF
* Your backend serializes the complete `paymentData.paymentMethodData` object once as a JSON string and sends it to `POST /v1/payments`, or tokenizes it first and sends the returned Plexo payment instrument token

## Required Google Pay Input

Set `paymentMethod.googlePayToken.paymentMethodData` to `JSON.stringify(paymentData.paymentMethodData)`: one required JSON string containing the entire Google Pay method object. Do not serialize the outer `paymentData` response or include its shipping or email fields in this string. This contract applies to direct payments, direct tokenization, and Google Pay session flows.

* The string must decode to an object with `type: "CARD"`.
* Its `tokenizationData` must be an object with `type: "PAYMENT_GATEWAY"` and a nonempty string `token`.
* Serialize the method object once. Do not serialize the resulting string again or Base64-encode it. Preserve the inner `tokenizationData.token` string unchanged; do not Base64-encode that token either.
* Missing, empty or malformed JSON strings, decoded arrays, nulls, primitives, double-encoded strings and encrypted-token-only JSON are rejected. Raw objects are not accepted as the public field value.
* `description` and `info` are optional and accept any JSON kind. Malformed display values are ignored; they do not invalidate an otherwise valid processing token. Do not make display fields required.
* Continue supplying cardholder details separately. Do not assume the wallet label identifies the cardholder.

{% hint style="warning" %}
**Breaking change:** `googlePayToken.paymentMethodData` is now a string, not an object. Update your wallet producer and backend mapping together. There is no object fallback or legacy token-only input. The request examples below use shortened, non-chargeable placeholders. See [GooglePayTokenDto](/rest-api/api-reference/models.md#googlepaytokendto) for the full schema.
{% endhint %}

## Official Google Resources

Follow these Google requirements when you render the Google Pay™ button in your own UI:

* [Google Pay Web Brand Guidelines](https://developers.google.com/pay/api/web/guides/brand-guidelines)
* [Google Pay Android Brand Guidelines](https://developers.google.com/pay/api/android/guides/brand-guidelines)
* [Google Pay Web Integration Checklist](https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist)
* [Google Pay Android Integration Checklist](https://developers.google.com/pay/api/android/guides/test-and-deploy/integration-checklist)

## Prerequisites

1. [Request Google Pay activation](/rest-api/wallets/google-pay.md#request-google-pay-activation) for the `api` channel in Plexo Dashboard
2. If you use `profileMode = plexo`, configure:
   * `googleMerchantName`
   * `googleMerchantOrigin` as a hostname without protocol
   * optional `allowedAuthMethods`
   * `allowedOrigins` as full origins with scheme
3. Call Plexo from your backend or BFF, not directly from browser code with Plexo API credentials

For `profileMode = plexo`, Plexo manages the runtime `merchantInfo.merchantId` automatically. TEST responses use Google Pay's `googletest` merchant ID. PRODUCTION responses use Plexo's managed production merchant ID.

## Authorization Methods and PAN\_ONLY 3DS

Plexo supports the two Google Pay card authorization methods returned by Google:

| Auth Method      | What It Means                                  | Plexo Handling                                                                                                  |
| ---------------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `CRYPTOGRAM_3DS` | Network token with device cryptogram           | Plexo routes the payment as a network-token payment. A new 3DS challenge is usually not required.               |
| `PAN_ONLY`       | PAN-based credential without device cryptogram | Plexo routes the payment as a card payment. 3DS depends on the merchant's standard 3DS configuration and rules. |

To expose both methods in the Google Pay sheet, select `PAN_ONLY` and `CRYPTOGRAM_3DS` for the `api` channel in **Integrations > Digital Wallets** in Plexo Dashboard. The Dashboard saves these values as `googlePayProfiles.api.allowedAuthMethods`.

If you omit `allowedAuthMethods`, TEST defaults to `["PAN_ONLY", "CRYPTOGRAM_3DS"]` and PRODUCTION defaults to `["CRYPTOGRAM_3DS"]`.

To have Plexo trigger 3DS for `PAN_ONLY` payments:

1. Include `PAN_ONLY` in `googlePayProfiles.api.allowedAuthMethods`.
2. Keep the merchant's standard 3DS configuration enabled in Plexo.
3. Make sure the payment matches the merchant's configured 3DS rules.

If those conditions are not met, Plexo returns `google-pay-pan-only-cvc-required`. Collect the payer's CVC and retry the payment with `paymentMethod.googlePayToken.cvc`, or `paymentMethod.paymentInstrument.cvc` if you already tokenized the wallet response.

Use the [Wallet Integrations API Reference](/rest-api/api-reference/wallet-integrations.md) to read the resulting channel availability. Configure activation in Plexo Dashboard.

## Step 0: Check Wallet Availability

Before loading the Google Pay SDK on your page, verify that Google Pay is enabled for the merchant. This avoids initializing the SDK unnecessarily when the wallet is not configured.

**Endpoint:** `GET /v1/merchants/{merchantId}/wallet-integrations`

```bash
curl -X GET "https://api.plexo.com.uy/v1/merchants/12345/wallet-integrations" \
  -u "username:password"
```

### Example Response

```json
{
  "googlePay": {
    "enabled": true,
    "channels": [{ "channel": "api", "enabled": true, "approved": true, "ready": true, "available": true, "status": "active" }]
  },
  "applePay": null
}
```

If `googlePay` is `null` or `enabled` is `false`, skip Google Pay SDK initialization entirely. Only proceed when its `api` channel is enabled, approved, ready, and available.

{% hint style="info" %}
Refresh availability before presenting checkout and after configuration or routing changes. A cached result does not authorize tokenization or payment; handle a current rejection even if Google Pay was previously available.
{% endhint %}

## Step 1: Fetch Runtime Configuration

Fetch Google Pay runtime configuration from your backend or BFF using the merchant's Plexo credentials.

**Endpoint:** `GET /v1/merchants/{merchantId}/google-pay/configuration`

If you expect `merchantInfo.authJwt`, forward the browser `Origin` header.

Preserve `merchantInfo` as returned by Plexo, including `merchantInfo.authJwt` when present. Do not move `authJwt` to the top level of a Google Pay request.

```bash
curl -X GET "https://api.plexo.com.uy/v1/merchants/12345/google-pay/configuration" \
  -u "username:password" \
  -H "Origin: https://pay.example.com"
```

### Example Response

```json
{
  "apiVersion": 2,
  "apiVersionMinor": 0,
  "environment": "PRODUCTION",
  "merchantInfo": {
    "merchantId": "BCR2DN4T2M6S7P6X",
    "merchantName": "Example Store",
    "merchantOrigin": "pay.example.com",
    "authJwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
  },
  "allowedPaymentMethods": [
    {
      "type": "CARD",
      "parameters": {
        "allowedCardNetworks": ["VISA", "MASTERCARD"],
        "allowedAuthMethods": ["CRYPTOGRAM_3DS"]
      },
      "tokenizationSpecification": {
        "type": "PAYMENT_GATEWAY",
        "parameters": {
          "gateway": "plexouy",
          "gatewayMerchantId": "12345"
        }
      }
    }
  ]
}
```

### Gateway Configuration

Use `allowedPaymentMethods[].tokenizationSpecification` exactly as returned by Plexo.

| Field                          | Value                                          |
| ------------------------------ | ---------------------------------------------- |
| `type`                         | `PAYMENT_GATEWAY`                              |
| `parameters.gateway`           | `plexouy`                                      |
| `parameters.gatewayMerchantId` | Your Plexo `merchantId` serialized as a string |

For example, merchant `12345` must use `"gatewayMerchantId": "12345"`. Plexo validates the decrypted wallet payload against this merchant ID, so do not replace it with a Google merchant ID or an internal store identifier.

### When `merchantInfo.authJwt` Is Returned

`merchantInfo.authJwt` is included only when all of these conditions are true:

1. `profileMode` is `plexo`
2. The `api` channel is enabled, approved, and ready
3. The request `Origin` matches one of the configured `allowedOrigins`
4. The runtime environment is `PRODUCTION`
5. Plexo has a managed Google Pay signing key configured

When `profileMode` is `merchant`, Plexo does not issue `merchantInfo.authJwt`.

`allowedPaymentMethods[].parameters.allowedAuthMethods` is driven by the merchant's dashboard configuration. If it has not been configured yet, TEST falls back to `["PAN_ONLY", "CRYPTOGRAM_3DS"]` and PRODUCTION falls back to `["CRYPTOGRAM_3DS"]`.

## Step 2: Initialize the Google Pay Button

Use the configuration returned by Plexo to initialize the Google Pay client and render the button.

Set `existingPaymentMethodRequired: true` only on the `isReadyToPay()` request for JWT integrations. It asks Google to include `paymentMethodPresent`, indicating whether the user has a saved payment method matching `allowedPaymentMethods`. That response field may be absent when cookies or cross-site tracking are unavailable; use `readiness.result` to check whether Google Pay can be used. See Google's [readiness request](https://developers.google.com/pay/api/web/reference/request-objects#IsReadyToPayRequest) and [response reference](https://developers.google.com/pay/api/web/reference/response-objects#IsReadyToPayResponse).

```html
<script async src="https://pay.google.com/gp/p/js/pay.js"></script>
<div id="google-pay-button"></div>

<script>
  let googlePayConfig;
  let paymentsClient;

  async function initGooglePay() {
    googlePayConfig = await fetch('/api/google-pay/configuration?merchantId=12345')
      .then((response) => response.json());

    paymentsClient = new google.payments.api.PaymentsClient({
      environment: googlePayConfig.environment,
      merchantInfo: googlePayConfig.merchantInfo,
    });

    const authJwt = googlePayConfig.merchantInfo.authJwt;
    const requiresJwtIntegration =
      googlePayConfig.environment === 'PRODUCTION' && Boolean(authJwt);

    const readinessRequest = {
      apiVersion: googlePayConfig.apiVersion,
      apiVersionMinor: googlePayConfig.apiVersionMinor,
      allowedPaymentMethods: googlePayConfig.allowedPaymentMethods,
      merchantInfo: requiresJwtIntegration ? googlePayConfig.merchantInfo : undefined,
      existingPaymentMethodRequired: requiresJwtIntegration,
    };

    const readiness = await paymentsClient.isReadyToPay(readinessRequest);
    if (!readiness.result) {
      return;
    }

    const button = paymentsClient.createButton({
      onClick: () => void onGooglePayButtonClicked(),
    });

    document.getElementById('google-pay-button').replaceChildren(button);
  }

  initGooglePay();
</script>
```

## Step 3: Load Payment Data and Send It to Your Server

The browser can send the entire `paymentData.paymentMethodData` object to your backend or BFF, as in this example. This is your internal request, not the public Plexo request. Serialize the method object once when mapping it to the public DTO; no nested DTO class is needed. Do not call Plexo directly from the browser with API credentials. Do not log or persist the wallet payload.

Pass `merchantInfo` unchanged, including its nested `authJwt` when present. Do not include `existingPaymentMethodRequired` in `loadPaymentData()`: it is not a `PaymentDataRequest` property and Android rejects it with `PaymentDataRequest.existingPaymentMethodRequired does not exist`. See Google's [payment request reference](https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest).

```javascript
async function onGooglePayButtonClicked() {
  const paymentDataRequest = {
    apiVersion: googlePayConfig.apiVersion,
    apiVersionMinor: googlePayConfig.apiVersionMinor,
    merchantInfo: googlePayConfig.merchantInfo,
    allowedPaymentMethods: googlePayConfig.allowedPaymentMethods.map((method) => ({
      ...method,
      parameters: {
        ...method.parameters,
        billingAddressRequired: true,
        billingAddressParameters: {
          format: 'FULL',
          phoneNumberRequired: true,
        },
      },
    })),
    transactionInfo: {
      totalPriceStatus: 'FINAL',
      totalPrice: '100.00',
      currencyCode: 'UYU',
      countryCode: 'UY',
    },
    emailRequired: true,
  };

  const paymentData = await paymentsClient.loadPaymentData(paymentDataRequest);

  await fetch('/api/google-pay/payments', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      referenceId: 'order-123',
      amount: {
        total: 100.0,
        currency: 'UYU',
      },
      transaction: {
        countryCode: 'UY',
      },
      googlePay: {
        paymentMethodData: paymentData.paymentMethodData,
        cardholder: {
          firstName: 'John',
          lastName: 'Doe',
          email: paymentData.email,
        },
      },
    }),
  });
}
```

## Step 4 (Optional): Tokenize for Installment Decisions

If your checkout flow needs to display installment options before charging, tokenize the Google Pay token first using `POST /v1/tokenizations`. The response includes `cardType` (credit, debit), `cardOrigin` (uruguay, international), and `issuer.countryCode` — use these to determine which installment plans to offer.

Map the object received in Step 3 to the public DTO, preserving its inner token string:

```javascript
const googlePayToken = {
  paymentMethodData: JSON.stringify(googlePay.paymentMethodData),
  cardholder: googlePay.cardholder,
};
```

Here, `googlePay` is the internal request's `googlePay` object from Step 3. Use this mapping for direct payments too. Your HTTP client serializes the enclosing request as usual; do not call `JSON.stringify` again on `googlePayToken.paymentMethodData`.

**Endpoint:** `POST /v1/tokenizations`

```json
{
  "merchantId": 12345,
  "type": "temporal",
  "paymentMethod": {
    "source": "google-pay-token",
    "walletChannel": "api",
    "googlePayToken": {
      "paymentMethodData": "{\"type\":\"CARD\",\"description\":\"Visa •••• 0027\",\"info\":{\"cardNetwork\":\"VISA\",\"cardDetails\":\"0027\"},\"tokenizationData\":{\"type\":\"PAYMENT_GATEWAY\",\"token\":\"{\\\"signature\\\":\\\"PLACEHOLDER\\\",\\\"protocolVersion\\\":\\\"ECv2\\\",\\\"signedMessage\\\":\\\"PLACEHOLDER\\\"}\"}}",
      "cardholder": {
        "firstName": "John",
        "lastName": "Doe",
        "email": "john.doe@example.com"
      }
    }
  }
}
```

### Example Response

```json
{
  "token": "9b7f1c2e-4a3d-4e8b-9c1f-2d3e4f5a6b7c",
  "paymentMethodId": "visa",
  "paymentMethodType": "card",
  "tokenizationType": "temporal",
  "status": "active",
  "source": "network-token",
  "bin": "41111111",
  "last4": "1111",
  "metadata": {
    "display_name": "Visa •••• 0027",
    "display_last4": "0027"
  },
  "expMonth": 12,
  "expYear": 2030,
  "cardType": "credit",
  "cardOrigin": "uruguay",
  "issuer": {
    "id": 101,
    "name": "Banco República Oriental del Uruguay",
    "shortName": "BROU",
    "country": "Uruguay",
    "countryCode": "UY"
  },
  "expiresAt": "2025-01-16T10:30:00Z"
}
```

After tokenization, use `cardType` and `cardOrigin` (or `issuer.countryCode`) combined with your merchant configuration to present the appropriate installment options. Then proceed to Step 5 using the returned `token` as `paymentMethod.paymentInstrument.token` with `paymentMethod.source = token`. Do not re-send the raw Google Pay token in the payment request when you use this branch.

{% hint style="info" %}
Skip this step if your flow does not offer installments. You can send the Google Pay token directly in Step 5 without tokenizing first.
{% endhint %}

## Step 5: Send the Payment to Plexo

Submit the payment through the Payments API using one of the two request shapes below.

**Endpoint:** `POST /v1/payments`

### Option A: Pay Directly With the Google Pay Token

**Intent:** Use this option when you do not need to tokenize before charging. This is the shortest payment path: your backend serializes the complete `paymentMethodData` object from Step 3 once and sends the JSON string in the payment request.

Use `paymentMethod.source = google-pay-token` and send the JSON string in `paymentMethod.googlePayToken.paymentMethodData`.

```json
{
  "merchantId": 12345,
  "referenceId": "order-123",
  "invoiceNumber": "INV-123",
  "amount": {
    "total": 100.00,
    "currency": "UYU"
  },
  "capture": {
    "method": "automatic"
  },
  "paymentMethod": {
    "source": "google-pay-token",
    "walletChannel": "api",
    "googlePayToken": {
      "paymentMethodData": "{\"type\":\"CARD\",\"description\":\"Visa •••• 0027\",\"info\":{\"cardNetwork\":\"VISA\",\"cardDetails\":\"0027\"},\"tokenizationData\":{\"type\":\"PAYMENT_GATEWAY\",\"token\":\"{\\\"signature\\\":\\\"PLACEHOLDER\\\",\\\"protocolVersion\\\":\\\"ECv2\\\",\\\"signedMessage\\\":\\\"PLACEHOLDER\\\"}\"}}",
      "cardholder": {
        "firstName": "John",
        "lastName": "Doe",
        "email": "john.doe@example.com"
      }
    }
  }
}
```

### Option B: Pay With the Token Returned by Step 4

**Intent:** Use this option when you tokenized the Google Pay payload first, usually to inspect card metadata and decide which installment plans to offer before creating the payment.

Use `paymentMethod.source = token` and send the token returned by `POST /v1/tokenizations` in `paymentMethod.paymentInstrument.token`. Send the selected installment count in `installments`.

```json
{
  "merchantId": 12345,
  "referenceId": "order-123",
  "invoiceNumber": "INV-123",
  "amount": {
    "total": 100.00,
    "currency": "UYU"
  },
  "capture": {
    "method": "automatic"
  },
  "installments": 3,
  "paymentMethod": {
    "source": "token",
    "paymentInstrument": {
      "token": "9b7f1c2e-4a3d-4e8b-9c1f-2d3e4f5a6b7c"
    }
  }
}
```

For `PAN_ONLY` Google Pay cards, Plexo may require a CVV instead of starting a 3DS flow. When that happens, retry the payment with either:

* `paymentMethod.googlePayToken.cvc` when you are sending the raw Google Pay token directly
* `paymentMethod.paymentInstrument.cvc` when you are using Option B

### Request Fields

| Field                                            | Type    | Required                                 | Description                                                                                                                          |
| ------------------------------------------------ | ------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `merchantId`                                     | integer | Yes                                      | Plexo merchant ID                                                                                                                    |
| `referenceId`                                    | string  | Yes                                      | Your transaction reference                                                                                                           |
| `amount.total`                                   | decimal | Yes                                      | Payment amount                                                                                                                       |
| `amount.currency`                                | string  | Yes                                      | ISO currency code                                                                                                                    |
| `installments`                                   | integer | No                                       | Selected installment count. Include this after Step 4 when your checkout presented installment options.                              |
| `paymentMethod.source`                           | string  | Yes                                      | Use `google-pay-token` when sending the raw wallet payload directly, or `token` when paying with the Plexo token returned by Step 4. |
| `paymentMethod.googlePayToken.paymentMethodData` | string  | Required for `source = google-pay-token` | Complete Google Pay method object serialized once as JSON; decoded processing shape described above.                                 |
| `paymentMethod.googlePayToken.cvc`               | string  | No                                       | CVV/CVC retry field for `PAN_ONLY` payments when Plexo returns `google-pay-pan-only-cvc-required`.                                   |
| `paymentMethod.googlePayToken.cardholder`        | object  | Required for `source = google-pay-token` | Cardholder details.                                                                                                                  |
| `paymentMethod.paymentInstrument.token`          | string  | Required for `source = token`            | Plexo payment instrument token returned by Step 4.                                                                                   |
| `paymentMethod.paymentInstrument.cvc`            | string  | No                                       | CVV/CVC retry field when a previously tokenized `PAN_ONLY` Google Pay payment requires CVV.                                          |

## Error Behavior

The exact error depends on the failure point. Common client-visible codes for this flow include:

| Error Code                         | When It Happens                                                                                                       |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `google-pay-not-enabled`           | Google Pay is not enabled for the merchant                                                                            |
| `google-pay-not-configured`        | The runtime configuration endpoint is called before the merchant profile is configured                                |
| `invalid-cardholder`               | The cardholder object is missing or invalid                                                                           |
| `request-validation-errors`        | Missing, null, empty or non-string `paymentMethodData`, request JSON cannot bind, or another request field is invalid |
| `invalid-card-payment-method`      | The Google Pay token cannot be used after validation or decryption checks                                             |
| `google-pay-pan-only-cvc-required` | A `PAN_ONLY` Google Pay payment must be retried with CVV because 3DS will not run                                     |
| `internal-error`                   | An unexpected backend failure occurs                                                                                  |

{% hint style="info" %}
Do not rely on invented uppercase codes such as `INVALID_GOOGLE_PAY_TOKEN`. Public API responses use the published error-code contract.
{% endhint %}

## Testing

For buyer-facing wallet identification, use normalized `metadata.display_last4` from the instrument or `paymentMethod.card.metadata.display_last4` from the payment, never processing `last4` as a fallback. See [wallet display hints](/rest-api/customers-and-saved-methods/tokenization-guide.md#wallet-display-hints).

Use Google Pay's [test card suite](https://developers.google.com/pay/api/android/guides/resources/test-card-suite) with your test merchant configuration.

## Next Steps

* [Google Pay Integration Guide](/rest-api/wallets/google-pay.md)
* [Digital Wallets Overview](/rest-api/wallets/digital-wallets.md)
* [Wallet Integrations API Reference](/rest-api/api-reference/wallet-integrations.md)
* [Payment Processing Guide](/rest-api/payments/payment-processing.md)
