Skip to main content

Google Pay™ Integration

Overview​

This guide walks you through accepting Google Pay™ on your website or Android app with NetValve. You build the Google paymentDataRequest yourself, point it at NetValve as the gateway, and send the encrypted token Google returns to NetValve on a normal /sale request. NetValve decrypts the token server-side and routes the transaction to the acquirer.

Read this first

For the operating model behind Google Pay at NetValve — including how NetValve's gateway registration with Google removes the need for you to manage any keys or certificates — read the Digital Wallets overview.

How NetValve handles certificates​

NetValve is registered with Google as the Google Pay gateway netvalve. That means you do not need to:

  • Obtain a Google Pay gateway certificate.
  • Generate or rotate a public/private key pair for tokenization.
  • Share any cryptographic material with NetValve.
  • Ask NetValve for a Google merchant ID, merchant name, or origin — these come from your own Google Pay & Wallet Console.

You configure your front-end to use NetValve as the gateway, send the resulting encrypted token to NetValve in the Sale request, and NetValve decrypts it server-side with its registered processor key before routing to the acquirer.

Before you start​

You need:

  • A NetValve siteId or netvalveMidId for the environment you are integrating against (sandbox or production). This is the value you already send on /sale.
  • A Google Pay & Wallet Console account. It is free, and it is where you get your Google merchant ID and register your production domain. NetValve is not involved in this step.

Google's own reference material for each platform:

PlatformDeveloper docsIntegration checklistBrand guidelines
WebGoogle Pay Web APIWeb integration checklistWeb brand guidelines
AndroidGoogle Pay Android APIAndroid integration checklistAndroid brand guidelines

Step 1 — Build the paymentDataRequest​

The paymentDataRequest is the object you pass to Google's PaymentsClient.loadPaymentData(). You build it entirely on your side; there is no NetValve call to fetch it. Below is a complete, working example for the web:

Complete paymentDataRequest for NetValve
const paymentDataRequest = {
apiVersion: 2,
apiVersionMinor: 0,

merchantInfo: {
// Your Google merchant ID from the Google Pay & Wallet Console (starts with BCR2).
// Ignored in TEST, required in PRODUCTION. NetValve does not issue this value.
merchantId: "BCR2DN4T...",
// Brand name shown on the Google Pay payment sheet.
merchantName: "Example Store"
},

allowedPaymentMethods: [
{
type: "CARD",
parameters: {
allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
allowedCardNetworks: ["AMEX", "DISCOVER", "MASTERCARD", "VISA"]
},
tokenizationSpecification: {
type: "PAYMENT_GATEWAY",
parameters: {
gateway: "netvalve",
// The siteId or netvalveMidId you will send on /sale. Must be identical.
gatewayMerchantId: "<your siteId or netvalveMidId>"
}
}
}
],

transactionInfo: {
// Your country of registration, ISO 3166-1 alpha-2.
countryCode: "US",
currencyCode: "USD",
totalPriceStatus: "FINAL",
totalPrice: "999.00"
}
};

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

paymentsClient.loadPaymentData(paymentDataRequest).then((paymentData) => {
// Google returns the encrypted token as a JSON string.
const googlePayToken = JSON.parse(paymentData.paymentMethodData.tokenizationData.token);
// Send googlePayToken to your backend, then to NetValve as googlePaySSL (Step 2).
});

Field reference​

FieldValueWhere it comes from
environment (on PaymentsClient)TEST in sandbox, PRODUCTION in productionYou
merchantInfo.merchantIdYour Google merchant ID (BCR2…). Ignored in TEST, required in PRODUCTION.Google Pay & Wallet Console — not issued by NetValve
merchantInfo.merchantNameBrand name shown on the payment sheetYou
transactionInfo.countryCodeYour country of registration (ISO 3166-1 alpha-2)You
transactionInfo.currencyCodeISO 4217 currency of the sale — same value as currency on /saleYou
transactionInfo.totalPriceAmount as a string with two decimals — same value as amount on /saleYou
transactionInfo.totalPriceStatusFINALFixed
allowedAuthMethods["PAN_ONLY", "CRYPTOGRAM_3DS"]Fixed — both are supported by NetValve
allowedCardNetworks["AMEX", "DISCOVER", "MASTERCARD", "VISA"]Fixed — the networks NetValve accepts through Google Pay
tokenizationSpecification.parameters.gatewaynetvalve (lowercase)Fixed
tokenizationSpecification.parameters.gatewayMerchantIdYour NetValve siteId or netvalveMidIdNetValve — the same value you send on /sale
gatewayMerchantId must match the /sale request

Google encrypts the token for the gatewayMerchantId you supply. NetValve then decrypts it against the siteId or netvalveMidId on the /sale request. If the two are not identical the sale fails with GTW_2096. Do not use your Google merchant ID, your NetValve client ID, or any other identifier here.

Android

The same structure applies on Android. Pass the identical tokenizationSpecification, allowedAuthMethods, allowedCardNetworks and transactionInfo values to PaymentsClient.loadPaymentData() in your app; only the request-building code differs. See Google's Android tutorial.

Step 2 — Send the token to NetValve​

Take the parsed token from paymentMethodData.tokenizationData.token and submit it as googlePaySSL on a standard Sale request, with paymentType: WALLET and walletType: GOOGLE_PAY. Everything else on the request is the same as a card sale.

POST /sale — Google Pay
{
"amount":999.00,
"cardHolderName":"John Doe",
"clientOrderId":{{clientOrderId}},
"currency":"USD",
"customerAddress":"123 Main St",
"customerCity":"New York",
"customerCountryCode":"US",
"customerEmail":"docs@netvalve.com",
"customerIp":"203.0.113.1",
"customerName":"John",
"customerLastName":"Doe",
"customerPhone":"+12025551234",
"customerState":"NY",
"customerZipCode":"10001",
"netvalveMidId":"{{netvalveMidId}}",
"paymentType":"WALLET",
"walletType":"GOOGLE_PAY",
"googlePaySSL":{
"signature":"MEUCIQDk+/hOm5zJmWrnmrG+ds+N5L28gMeE0drnDyjyvCnX5wIgAloe3rwAPu7tMwawefcGYO4G6m/+ilQo1DqnphVIJ/s=",
"intermediateSigningKey":{
"signedKey":"{\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0NSQX6HDOtKu3B2TI9HJV3IjiRQdebbCcDR9lVkx51TeIZ/J8lGDFcpqZTkNED1g5QXgTqq5ALpG+d43cQRaQA\\u003d\\u003d\",\"keyExpiration\":\"1715897257675\"}",
"signatures":[
"MEYCIQCUGkX7SGY6WG94ZHuKfyQpHG6o2KbL1gYmB8aZPRebmwIhAMH61R36CiPbPhY3oohvt6pw3ZQJkYDWFDB6SHpFABTA"
]
},
"protocolVersion":"ECv2",
"signedMessage":"{\"encryptedMessage\":\"Syk7ZTw7KzXS6rEisGPZlHTEgpIlH5Z5HJktPKcYHrPWcVh+d4l1MroajG5NQPEOgYxlZV9A4XXq2ZdM/2H/wge9aSCDzY9iY8m2JCMK1SoZVrnD5+dy66iG93VFuAdUjf+ZiddsWDRR1cLyyz/IudsNKaIKKzJNFHk+pCFktklMhvSSaGoDJbKxMqEDuipk6E+R2sAEkBBuKD8zdlzn1xheLnv+MMULUqe7XJTqcCiAJajqMBTFpnyQX3gxd28QwFKu2FX2mYsviR7VsxPnETlSvnabHpcxkLv8EL0mqkGM5hicmjfjEbEKeb2ZXkULBTxipfeHf5eSXo+MyCoo8W5Q+8SvwE9x22KaxJy0A3vPAfgQOK568y+riM+yfP7xt5lWCrCr0rKhepeT+JDEZHdT3gUMlMLdmtzlyT7r6ddtftMkTYzq5BneBFHqYl7uryRh9lFsjSrDjoZQjUGzASPQX+s3ty593Y/1t88fXCNV2xf3PHkpMFq9ZaTKwv1XyHs0uXgawVr2+DVnXcqxfteM5GvpxN42EDlz\",\"ephemeralPublicKey\":\"BOmikxGNXx91S5MftOqKFwqbpiGUr8HfN0zAry5iN4T9LtwtklyG7vS1BsU2YJFaggu5GmuLhA9vFgfvKIPnNUI\\u003d\",\"tag\":\"kJSe9C2RkVCpGt5WCzIZgYnBbywF+pc5LdlUzo43NZw\\u003d\"}"
}
}
  • netvalveMidId (or siteId) must be the same value you used as gatewayMerchantId in Step 1.
  • amount and currency should match transactionInfo.totalPrice and transactionInfo.currencyCode.
  • A Google Pay token is single-use. Request a new one for every sale attempt.
Already decrypting the token yourself?

If you decrypt the Google Pay token on your own backend, you can skip googlePaySSL and send the decrypted card-network fields directly in a tokenizedDTO object instead. See Submitting a decrypted payload.

Example response​

A successful sale returns responseCode: GTW_1000:

{
"traceID": "b126345b-6199-4635-9bf2-f9aadbdd4d58",
"responseTimestamp": "2024-11-08T12:57:41.771+00:00",
"transactionID": 18749,
"responseCode": "GTW_1000",
"responseMessage": "Transaction Approved/ Request Successful.",
"responseCodeType": "APPROVED",
"paymentMethod": "GOOGLE_PAY",
"cardNumber": "411111******1111",
"cardType": "VISA",
"bankTransactionId": "431312750166",
"authCode": "TAS213",
"midId": 2,
"netvalveMidId": "289e253d-f955-4e29-a2c7-bb1805883ee0"
}

Step 3 — Going live​

NetValve needs nothing from you to go live with Google Pay. All production setup happens in your own Google Pay & Wallet Console:

  1. Register your production domain in the Google Pay & Wallet Console and get it approved. Google will not show the payment sheet in PRODUCTION on an unregistered origin.
  2. Complete Google's integration checklist for your platform (web / Android) and request production access from the console.
  3. Switch the request to production: set environment: "PRODUCTION" on PaymentsClient, and set merchantInfo.merchantId to your BCR2… merchant ID. It is required in production.
  4. Use your production NetValve identifiers: gatewayMerchantId and the /sale siteId / netvalveMidId must both be the production values, and /sale must go to the production API base URL. Sandbox and production are separate NetValve environments with separate MIDs.
  5. Do not send line items in production. Leave transactionInfo.displayItems and totalPriceLabel out of the request. Line items in a production request trigger OR_BIBED_06 on Google's payment sheet.

Google Pay must also be enabled on your NetValve MID. If it is not yet enabled, ask your NetValve representative — this is the only NetValve-side setting involved.

Errors​

CodeWhere you see itMeaningFix
GTW_2096NetValve /sale responseNetValve could not decrypt the Google Pay token. Almost always a gatewayMerchantId mismatch: the token was encrypted for a different siteId / netvalveMidId than the one on the /sale request. Also returned for a reused, expired, or malformed token.Make gatewayMerchantId identical to the /sale siteId / netvalveMidId. Check gateway is lowercase netvalve. Request a fresh token for each attempt and send the parsed JSON object, not the raw string.
OR_BIBED_06Google's payment sheet, before any NetValve callGoogle rejected the request because of a merchant-profile or line-item problem. Common causes: displayItems present in a PRODUCTION request, merchantInfo.merchantId missing or not matching your console, or the origin is not registered/approved in the Google Pay & Wallet Console.Remove displayItems and totalPriceLabel. Verify merchantId and your registered domain in the Google Pay & Wallet Console. This error is on the Google side; NetValve cannot see or change it.

Standard NetValve response codes (declines, validation errors, routing) apply to Google Pay sales the same way they do to card sales. See Error codes.

Submitting a decrypted payload (tokenizedDTO)​

The googlePaySSL flow above hands NetValve the encrypted Google Pay token and lets NetValve decrypt it server-side. If you'd rather decrypt the token yourself and pass NetValve the resulting card-network data, send a tokenizedDTO object in place of googlePaySSL. Everything else about the Sale request is identical — same endpoint, same paymentType: WALLET, same walletType: GOOGLE_PAY.

This is useful when you already decrypt the Google Pay token on your backend (for example because you hold your own gateway/tokenization keys) and want a single integration path across processors.

Send either googlePaySSL or tokenizedDTO — not both. If tokenizedDTO is present, NetValve treats the wallet payload as already decrypted and does not attempt to decrypt anything.

Two authentication methods​

Unlike Apple Pay, Google Pay decrypts into one of two authentication methods, and the fields you send differ between them. Set authMethod accordingly:

authMethodWhat cardNumber holdscryptogram / eciIndicator
CRYPTOGRAM_3DSA network token (DPAN)Required — the online cryptogram and ECI from the decrypted token.
PAN_ONLYThe real card number (FPAN)Omit — there is no cryptogram or ECI for this method.

Sending the correct authMethod matters: some acquirers route PAN_ONLY transactions differently, and a PAN_ONLY payload that carries a cryptogram (or vice versa) may be rejected.

Where the fields come from​

After you decrypt the Google Pay token, the paymentMethodDetails object looks like this (Google's fields shown for reference):

Decrypted Google Pay payment method details (Google's format)
{
"paymentMethodDetails": {
"authMethod": "CRYPTOGRAM_3DS",
"pan": "4831961250220480",
"expirationMonth": 12,
"expirationYear": 2028,
"cryptogram": "Az2Uq7EABOvUpHEqLyFuMAACAAA=",
"eciIndicator": "05"
}
}

Map those into tokenizedDTO as follows:

tokenizedDTO fieldSource in the decrypted tokenNotes
cardNumberpanNetwork token (DPAN) for CRYPTOGRAM_3DS; the real card number (FPAN) for PAN_ONLY.
cardExpiryMonthexpirationMonthAlready a discrete month (MM) — no parsing needed, unlike Apple's combined date.
cardExpiryYearexpirationYearAlready YYYY.
cryptogramcryptogramCRYPTOGRAM_3DS only. Passed to the acquirer as the online cryptogram (AAV/TAVV) for Visa and Mastercard. Omit for PAN_ONLY.
eciIndicatoreciIndicatorCRYPTOGRAM_3DS only. Omit for PAN_ONLY.
authMethodauthMethodCRYPTOGRAM_3DS or PAN_ONLY.

Example — CRYPTOGRAM_3DS​

Sale request with a decrypted Google Pay payload (CRYPTOGRAM_3DS)
{
"amount": 11.00,
"clientOrderId": {{clientOrderId}},
"currency": "USD",
"customerName": "John",
"customerLastName": "Doe",
"customerEmail": "docs@netvalve.com",
"netvalveMidId": "{{netvalveMidId}}",
"paymentType": "WALLET",
"walletType": "GOOGLE_PAY",
"tokenizedDTO": {
"cardExpiryMonth": "12",
"cardExpiryYear": "2028",
"cardNumber": "4831961250220480",
"authMethod": "CRYPTOGRAM_3DS",
"cryptogram": "Az2Uq7EABOvUpHEqLyFuMAACAAA=",
"eciIndicator": "05"
}
}

Example — PAN_ONLY​

No cryptogram or ECI, and cardNumber is the real card number:

Sale request with a decrypted Google Pay payload (PAN_ONLY)
{
"amount": 11.00,
"clientOrderId": {{clientOrderId}},
"currency": "USD",
"customerName": "John",
"customerLastName": "Doe",
"customerEmail": "docs@netvalve.com",
"netvalveMidId": "{{netvalveMidId}}",
"paymentType": "WALLET",
"walletType": "GOOGLE_PAY",
"tokenizedDTO": {
"cardExpiryMonth": "12",
"cardExpiryYear": "2028",
"cardNumber": "4012000098765439",
"authMethod": "PAN_ONLY"
}
}
note

The same tokenizedDTO shape is used for Apple Pay and Samsung Pay — only walletType (and, for Apple, the always-present cryptogram) changes. See the Apple Pay guide and the Digital Wallets overview.

Card Parameters​

Google Pay™ transactions do not require a billing address. Therefore, no billing address parameters need to be provided with a card.

Supported Card Authentication Methods​

Google Pay™ supports the following card authentication methods:

  • PAN_ONLY;
  • CRYPTOGRAM_3DS.

Supported Card Networks​

Google Pay™ supports transactions with cards from the following networks:

  • AMEX;
  • DISCOVER;
  • MASTERCARD;
  • VISA.

Enable Google Pay on Hosted Payment Page​

If you use NetValve's Hosted Payment Page, Google Pay can be enabled per MID from the Backoffice — no keys or certificates to upload. The HPP uses NetValve's registered gateway with Google, so once Google Pay is enabled on your MID the button will appear automatically on supported devices.

Enable Google Pay on the Checkout SDK​

If you use the Checkout SDK, the SDK loads Google's pay.js, builds the paymentDataRequest with the right gateway parameters and submits the Sale for you, so you skip the steps above. Once GOOGLEPAY is enabled on your site and your CSP allows https://pay.google.com, the button appears on supported devices. See Apple Pay and Google Pay in the SDK guide.

Testing​

Google Pay™ is available in the NetValve sandbox. Use environment: "TEST" on PaymentsClient and your sandbox siteId / netvalveMidId as gatewayMerchantId. In TEST, merchantInfo.merchantId is ignored and no domain registration is needed, so you can test from localhost or any staging host.

To pay with Google's test cards on the sheet, join Google's test card group. Once you are a member, test cards appear automatically in the TEST payment sheet for your Google account.

Generate a test token without writing front-end code​

If you want to exercise /sale before your front-end is ready, Google's token generator produces a real TEST token you can paste into the request.

Google token generator

Fill the fields following these guidelines:

  • Gateway ID: netvalve;
  • Gateway Merchant ID: your sandbox siteId or netvalveMidId — the same value you will send on /sale;
  • Join the test card group so Google's test cards are available on the sheet;
  • Click the Apply button, then go to the next section and click the Buy with G Pay button:

Google Pay button

  • Choose the payment method and click the Continue button:

Google Pay test payment

  • As a response, Google will provide the PaymentData item. There, in the paymentMethodData.tokenizationData.token field, you can find the encrypted Google Pay Token (a string of characters).
Sample Google Pay Token
{
"signature": "MEUCIQDY3wBQyHB4sZcktRoJXKxm+OLcjHzCvdDeGn23oX0kkwIgKznRFZZL+sDMv1b5cuD+YurXMZraYBsr9hbravVY5Ro=",
"protocolVersion": "ECv1",
"signedMessage": "{\"encryptedMessage\":\"cI87tLqzqTGyCFnMMCVWcTHw3xhYIK+CEnuQ74K+nlLpCgOlfpScib9jds4sxDtN6CunCqCSMfd/3yHeeRy6aCx1yyqcT4ey6NueeBznprJpkmVVgI1JHWLQt4hzAXMUAcYASYLOabKP9fUZvHkOBDytD531jpzNXa+Spc/zrpGzFKx2C4VU9sC95q9i+ey+kr7ZMNVCOFJPWXu7lKZ105IOOqozJ6/70MKmxP3jM89eeq+/19QnyHjQLXfnQPvQjiUJKGCcRKDLlrb3XoY5ZUUzGfN5eZCLzCVg0hWEbwU+6J7KWYJyW+Wr1r8bagN9zWsrMKhDpsQbHfyzb+yBzFUoxeUgL4a7FeVvEllIcHtqsvTCf6FENV20aF5VLDv5qzUkV+PzTAIbFEuabA0God9UbVCVVv7nM8QFzvRPhzYYFVFTn4JHvL2qZ4pAR9lE+w\\u003d\\u003d\",\"ephemeralPublicKey\":\"BPHLC4sBHpenY1M0ixmiDMuWJTaTJOqggRUwtgBJMcBp28VsxHD7zPI7985x4F5EjMP5y8j/cuUzbe/cGPjOKGk\\u003d\",\"tag\":\"RaXrPOUuc5iw3oxDa0C2MOjaKxgxIRQvwOspmtFV0zU\\u003d\"}"
}

Copy the token and insert it as the googlePaySSL object in your /sale request (see Step 2):

Insert Token