---
title: Universal Commerce Protocol integration with PayPal
description: Process Google Pay credentials from Google AI Mode checkout
  sessions with the Orders v2 API.
---

# Universal Commerce Protocol integration with PayPal (/agent-ready/universal-commerce-protocol-with-paypal)



Use this guide to integrate the Google Pay payment handler for Google's Universal Commerce Protocol (UCP) with PayPal as your payment provider. This supports AI-powered checkout experiences through Google AI Mode (Gemini), where Google Pay returns a tokenized credential that PayPal processes through the [Orders v2 API](/api/orders/v2). This guide applies to PayPal merchants on the direct, connected, and embedded integration paths.

## How it works [#how-it-works]

In a standard Google Pay integration, tokenization happens client-side. With UCP, Google moves it to the server side. Google sends the encrypted payment credential in the checkout session in ECv2 format. You don't decrypt or parse the credential on your server. Instead, you forward it to PayPal, which decrypts it server-side.

## Prerequisites [#prerequisites]

* You have an active PayPal merchant account.
* Google Pay is enabled for your merchant account on PayPal.
* You have completed UCP merchant registration with Google. Contact Google to request enablement.
* You are familiar with the PayPal [Orders v2 API](/api/orders/v2) and the `payment_source.google_pay` object.

## Integration steps [#integration-steps]

Complete these three steps to integrate the Google Pay payment handler for UCP with PayPal: configure the handler, handle the checkout payload from Google, and create an order with the [Orders v2 API](/api/orders/v2).

### Step 1: Configure the Google Pay payment handler tokenization [#step-1-configure-the-google-pay-payment-handler-tokenization]

When you register your UCP profile with Google, define the Google Pay payment handler. Set `tokenization_specification` to the `PAYMENT_GATEWAY` type, which uses the same structure as the configuration that `paypal.Googlepay().config()` already returns:

```json
{
  "payment_handlers": {
    "com.google.pay": [
      {
        "id": "YOUR_HANDLER_UUID",
        "version": "2026-01-23",
        "spec": "https://pay.google.com/gp/p/ucp/2026-01-23/",
        "schema": "https://pay.google.com/gp/p/ucp/2026-01-23/schemas/config.json",
        "config": {
          "api_version": 2,
          "api_version_minor": 0,
          "environment": "PRODUCTION",
          "merchant_info": {
            "merchant_name": "YOUR_BUSINESS_NAME",
            "merchant_id": "BCR2DN4TXSDMVTKM", // PayPal’s Google Merchant Id
            "merchant_origin": "checkout.yourdomain.com"
          },
          "allowed_payment_methods": [
            {
              "type": "CARD",
              "parameters": {
                "allowed_auth_methods": ["PAN_ONLY"],
                "allowed_card_networks": ["AMEX", "DISCOVER", "MASTERCARD", "VISA"],
                "assurance_details_required": true,
                "billing_address_required": true,
                "billing_address_parameters": { "format": "FULL", "phone_number_required": false }
              },
              "tokenization_specification": {
                "type": "PAYMENT_GATEWAY",
                "parameters": {
                  "gateway": "paypalppcp",
                  "gatewayMerchantId": "YOUR_PAYPAL_MERCHANT_ID"
                }
              }
            }
          ]
        }
      }
    ]
  }
}
```

The following table describes the parameters in the sample that identify PayPal as the payment gateway.

| Parameter                   | Description                                                                                                                                                           |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `gateway`                   | Set to `paypalppcp`. This is PayPal's registered Google Pay gateway, and the value is the same for all PayPal merchants.                                              |
| `gatewayMerchantId`         | The PayPal encrypted merchant account ID for the individual merchant. This value is different for each merchant.                                                      |
| `merchant_info.merchant_id` | Set to `BCR2DN4TXSDMVTKM`. This is Google's registered gateway ID for PayPal. The value is the same for all merchants and identifies PayPal as the gateway to Google. |

The `allowed_auth_methods` value must be `PAN_ONLY`. This is the only value that Google's UCP config schema supports. The credential that Google returns is an FPAN that contains the card number and expiration date. `CRYPTOGRAM_3DS` is not part of the UCP config.

The PayPal `tokenization_specification` requires only `gateway` and `gatewayMerchantId`. You don't need a tokenization key, an SDK version, or other namespaced parameters. PayPal relies on the `paypalppcp` gateway registration and its own recipient private keys to decrypt the credential.

### Step 2: Handle the checkout payload [#step-2-handle-the-checkout-payload]

When Google calls your `/checkout-session/{id}/complete` endpoint, the payment method's `credential.token` is the ECv2 gateway payload, a JSON-serialized string:

```json
{
  "payment": {
    "instruments": [
      {
        "id": "pm_1234567890abc",
        "handler_id": "YOUR_HANDLER_UUID",
        "type": "card",
        "display": { "brand": "VISA", "last_digits": "4242" },
        "billing_address": {
          "street_address": "123 Main Street",
          "address_locality": "Charleston",
          "address_region": "SC",
          "postal_code": "29401",
          "address_country": "US",
          "first_name": "Jane",
          "last_name": "Smith"
        },
        "credential": {
          "type": "PAYMENT_GATEWAY",
          "token": "{\"signature\":\"...\",\"protocolVersion\":\"ECv2\",\"signedMessage\":\"...\"}"
        }
      }
    ]
  },
  "risk_signals": { }
}
```

Don't parse or decrypt `credential.token` on your server. Forward it to PayPal exactly as you receive it, as described in Step 3.

### Step 3: Use Order v2 API to process the payment with PayPal [#step-3-use-order-v2-api-to-process-the-payment-with-paypal]

Create a PayPal order with the Google Pay credential set inline on `payment_source.google_pay`. Because the credential is an inline Google Pay token, PayPal can process it during the create call. You don't need a separate confirm-payment-source call.

PayPal decrypts the token server-side, builds the funding instrument, and authorizes or captures the payment in a single request. Set `intent` to one of these values:

* `CAPTURE`: Creates the order and captures the payment.
* `AUTHORIZE`: Creates the order and authorizes the payment. You capture it later.

The `payment_source.google_pay` object supports these fields: `token`, `decrypted_token`, `name`, `email_address`, `phone_number`, `card`, `assurance_details`, and `experience_context`. In the request, `card` carries only `brand` and `billing_address`.

You provide the credential in one of two fields:

* `token`: The encrypted ECv2 payload from Google. PayPal decrypts it.
* `decrypted_token`: Use this field only if you have already decrypted the credential yourself. It doesn't apply to this integration, because PayPal decrypts the credential.

#### Create an order request [#create-an-order-request]

Send a POST request to `/v2/checkout/orders` with the Google Pay credential in the `payment_source.google_pay` object. This single call creates the order and captures the payment.

```json
{
  "intent": "CAPTURE",  // AUTHORIZE for create + auth (capture later)
  "purchase_units": [
    {
      "amount": { "currency_code": "USD", "value": "120.00" }
    }
  ],
  "payment_source": {
    "google_pay": {
      "token": "<base64-encoded credential.token from Google>"
    }
  }
}
```

#### Base64-encode the token [#base64-encode-the-token]

Base64-encode the `credential.token` value before you send it. Google delivers `credential.token` as a JSON string, so encode that string directly and put the result in `payment_source.google_pay.token`. PayPal decodes and decrypts it server-side.

Don't run `JSON.stringify` on the token first. Doing so double-encodes the value, and PayPal can't parse it. In JavaScript, use `Buffer.from(credentialToken).toString('base64')`.

#### Card and billing address fields [#card-and-billing-address-fields]

In the request, `google_pay.card` carries only `brand` and `billing_address`. Don't send `number`, `expiry`, or `last_digits`. PayPal derives them from the decrypted token and returns `last_digits`, `type`, and `fingerprint` in the response.

Billing address fields use PayPal's portable address names:

* `address_line_1`
* `address_line_2`
* `admin_area_2`: City
* `admin_area_1`: State or province
* `postal_code`
* `country_code`

#### Request headers [#request-headers]

Include these headers:

* `Prefer: return=representation`: Includes the `captures` array in the response.
* `PayPal-Request-Id`: A unique value that you generate for each request. If you retry a request with the same value, PayPal doesn't create a duplicate order.

#### Response [#response]

A successful request returns `201 Created` with the completed order. The capture is nested under the purchase unit:

```json
{
  "id": "8MW02486UJ231741T",              // ORDER id
  "intent": "CAPTURE",
  "status": "COMPLETED",
  "payment_source": {
    "google_pay": {
      "card": { "last_digits": "8960", "type": "CREDIT", "brand": "VISA" }
    }
  },
  "purchase_units": [
    {
      "payments": {
        "captures": [
          {
            "id": "7FD40336T0573644K",     // CAPTURE id
            "status": "COMPLETED",
            "amount": { "currency_code": "USD", "value": "120.00" },
            "final_capture": true
          }
        ]
      }
    }
  ]
}
```
