Payment data cryptography for merchants

The Google Pay API returns payment methods in a signed and encrypted PaymentMethodToken payload. The returned payment methods are either cards that consist of PAN, or tokenized cards that consist of device PAN and cryptograms.

The payload contains a field called protocolVersion that tells the recipient of the payload which cryptographic primitives are in use and the expected format.

This guide provides information on how to generate a public key to request a Google-signed and encrypted payment method token, and details the steps to take to verify and decrypt the token.

This guide applies only to protocolVersion = ECv2.

Because you receive payment card information directly, make sure your app is PCI DSS compliant and that your servers have the required infrastructure to securely handle the user's payment credentials before you proceed.

The following steps outline what an integrator must do to consume the Google Pay API ECv2 PaymentMethodToken payload:

  1. Fetch the Google root signing keys.
  2. Verify that the signature of the intermediate signing key is valid by any of the non-expired root signing keys.
  3. Verify that the intermediate signing key of the payload hasn't expired.
  4. Verify that the signature of the payload is valid by the intermediate signing key.
  5. Decrypt the contents of the payload after you verify the signature.
  6. Verify that the message isn't expired. This requires you to check that the current time is less than the messageExpiration field in the decrypted contents.
  7. Use the payment method in the decrypted contents and charge it.

The sample code in our Tink library performs steps 1–6.

Payment method token structure

The message returned by Google in the PaymentData response is a UTF-8 encoded, serialized JSON object with the keys specified in the following table:

Name Type Description
protocolVersion String Identifies the encryption or signing scheme under which the message is created. It allows the protocol to evolve over time, if needed.
signature String Verifies that the message came from Google. It's base64-encoded, and created with ECDSA by the intermediate signing key.
intermediateSigningKey Object A JSON object that contains the intermediate signing key from Google. It contains the signedKey with keyValue, keyExpiration, and signatures. It's serialized to simplify the intermediate signing key signature verification process.
signedMessage String A JSON object serialized as an HTML-safe string that contains the encryptedMessage, ephemeralPublicKey, and tag. It's serialized to simplify the signature verification process.

Example

The following is a payment method token response in JSON:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

Intermediate signing key

The intermediateSigningKey is a UTF-8 encoded, serialized JSON object that contains the following values:

Name Type Description
signedKey String A base64-encoded message that contains payment description of the key.
signatures String Verifies that the intermediate signing key came from Google. It's base64-encoded, and created with ECDSA.

Signed key

The signedKey is a UTF-8 encoded, serialized JSON object that contains the following values:

Name Type Description
keyValue String A base64 version of key encoded in ASN.1 type. SubjectPublicKeyInfo is defined in the X.509 standard.
keyExpiration String Date and time when the intermediate key expires as UTC milliseconds since epoch. Integrators reject any key that's expired.

Signed message

The signedMessage is a UTF-8 encoded, serialized JSON object that contains the following values:

Name Type Description
encryptedMessage String A base64-encoded encrypted message that contains payment information and some additional security fields.
ephemeralPublicKey String A base64-encoded ephemeral public key associated with the private key to encrypt the message in uncompressed point format. For more information, see Encryption public key format.
tag String A base64-encoded MAC of encryptedMessage.

Encrypted message

The decrypted encryptedMessage is a UTF-8 encoded, serialized JSON object. The JSON contains two levels. The outer level contains metadata and fields included for security, while the inner level is another JSON object that represents the actual payment credential.

For more details about encryptedMessage, see the following tables and JSON object examples:

Name Type Description
messageExpiration String Date and time at which the message expires as UTC milliseconds since epoch. Integrators should reject any message that's expired.
messageId String A unique ID that identifies the message in case it needs to be revoked or located at a later time.
paymentMethod String The type of the payment credential. Currently, only CARD is supported.
paymentMethodDetails Object The payment credential itself. The format of this object is determined by the paymentMethod and is described in the following tables.

Card

The following properties make up a payment credential for the CARD payment method:

Name Type Description
pan String The personal account number charged. This string contains only digits.
expirationMonth Number The expiration month of the card, where 1 represents January, 2 represents February, and so on.
expirationYear Number The four-digit expiration year of the card, such as 2020.
authMethod String The authentication method of the card transaction.

PAN_ONLY

The following JSON snippet is an example of the full encryptedMessage for a CARD paymentMethod with a PAN_ONLY authMethod.

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "PAN_ONLY",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2025
  },
  "gatewayMerchantId": "some-merchant-id",
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

CRYPTOGRAM_3DS

A CARD authenticated with the use of a 3-D Secure cryptogram, CRYPTOGRAM_3DS authMethod. It includes the following additional fields:

Name Type Description
cryptogram String A 3-D Secure cryptogram.
eciIndicator String This string isn’t always present. It returns only for authenticated device tokens transactions on Android (CRYPTOGRAM_3DS). This value must be passed down the payment processing flow.

The following JSON snippet is an example of the full encryptedMessage for a CARD paymentMethod with a CRYPTOGRAM_3DS authMethod:

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "CRYPTOGRAM_3DS",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2025,
    "cryptogram": "AAAAAA...",
    "eciIndicator": "eci indicator"
    
  },
  
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

eciIndicator

The card network might provide the eciIndicator for authenticated device tokens transactions (CRYPTOGRAM_3DS).

You must pass the eciIndicator value on the authorization transaction without it being altered or hardcoded; otherwise, the transaction fails. The following table details the values of the eciIndicator.

eciIndicator value Card Network Liable Party authMethod
""(empty) Mastercard Merchant/Acquirer CRYPTOGRAM_3DS
02 Mastercard Card issuer CRYPTOGRAM_3DS
06 Mastercard Merchant/Acquirer CRYPTOGRAM_3DS
05 Visa Card issuer CRYPTOGRAM_3DS
07 Visa Merchant/Acquirer CRYPTOGRAM_3DS
""(empty) Other networks Merchant/Acquirer CRYPTOGRAM_3DS

Any other ECI values for VISA and Mastercard that aren't present in this table won't be returned.

Signature verification

To verify the signatures, which include the intermediate key and message signatures, the following items are required:

  • The algorithm used to create the signature
  • The byte-string used to create the signature
  • The public key that corresponds to the private one used to create the signature
  • The signature itself

The signature algorithm

Google uses the Elliptic Curve Digital Signature Algorithm (ECDSA) to sign the messages with the following parameters: ECDSA over NIST P-256 with SHA-256 as the hash function, as defined in FIPS 186-4.

The signature

The signature is included in the outermost level of the message. It's encoded with base64 in ASN.1 byte format. For more information on ASN.1, see IETF Tools Appendix A. The signature consists of the ECDSA integers r and s. For more information, see Signature generation algorithm.

The following is an example of the specified ASN.1 byte format, which is the standard format produced by Java Cryptography Extension (JCE) ECDSA implementations.

ECDSA-Sig-Value :: = SEQUENCE {
 r INTEGER,
 s INTEGER
}

How to construct the byte-string for intermediate signing key signature

To validate the intermediate signing key signature in the sample payment method token, construct the signedStringForIntermediateSigningKeySignature with the following formula:

signedStringForIntermediateSigningKeySignature =
length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key

The "||" notation means concatenate. Each component—sender_id, protocolVersion, signedKey—must be UTF-8 encoded. The signedKey must be the string of intermediateSigningKey.signedKey. The byte length of each component is 4 bytes in little-endian format.

Example

This example uses the following sample payment method token:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

The sender_id is always Google and the protocol_version is ECv2.

If the sender_id is Google, the signedString appears as shown in the following example:

signedStringForIntermediateSigningKeySignature =
\x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}

How to verify the signature on signedStringForIntermediateSigningKeySignature

The standard ECDSA verification algorithm is used when the signed string for the intermediate signing key signature is assembled. For ECv2 protocol, you need to iterate over all the signatures in intermediateSigningKey.signatures and try to validate each one with the non-expired Google signing keys in keys.json. If at least one signature validation works, consider the verification complete. Use the intermediateSigningKey.signedKey.keyValue later to verify the signedStringForMessageSignature. Google strongly recommends you use a cryptographic library that exists rather than your own verification code.

How to construct the byte-string for message signature

To validate the signature in the sample payment method token, construct the signedStringForMessageSignature with the following formula:

signedStringForMessageSignature =
length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage

The "||" notation means concatenate. Each component—sender_id, recipient_id, protocolVersion, signedMessage—must be UTF-8 encoded. The byte length of each component is 4 bytes in little-endian format. When you construct the byte string, don't parse or modify signedMessage. For example, don't replace \u003d with the = character.

Example

The following example is a sample payment method token:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

The sender_id is always Google and the recipient_id is merchant:merchantId. The merchantId matches the value found in the Google Pay & Wallet Console for merchants with production access.

If the sender_id is Google and the recipient_id is merchant:12345, the signedString appears as it is in the following example:

signedStringForMessageSignature =
\x06\x00\x00\x00 || Google || \x0e\x00\x00\x00 || merchant:12345 || | \x04\x00\x00\x00 || ECv2 || \xd2\x00\x00\x00 || {"tag":"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\u003d","ephemeralPublicKey":"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\u003d","encryptedMessage":"mKOoXwi8OavZ"}

How to verify the signature on signedStringForMessageSignature

The standard ECDSA verification algorithm is used when the signed string is assembled. The intermediateSigningKey.signedKey.keyValue verified in the previous step is used to verify the signedMessage. Google strongly recommends you use a cryptographic library that exists rather than your own verification code.

Encryption scheme specification

Google uses the Elliptic Curve Integrated Encryption Scheme (ECIES) to secure the payment method token returned in the Google Pay API response. The encryption scheme uses the following parameters:

Parameter Definition
Key encapsulation method

ECIES-KEM, as defined in ISO 18033-2.

  • Elliptic curve: NIST P-256 (also known in OpenSSL as prime256v1).
  • CheckMode, OldCofactorMode, SingleHashMode, and CofactorMode are 0.
  • Point format is uncompressed.
Key derivation function

HMAC-based with SHA-256 (HKDFwithSHA256).

  • Salt must not be provided.
  • Information must be Google-encoded in ASCII for protocol version ECv2.
  • 256 bits must be derived for the AES256 key and another 256 bits must be derived for the HMAC_SHA256 key.
Symmetric encryption algorithm

DEM2, as defined in ISO 18033-2

Encryption algorithm: AES-256-CTR with zero IV and not padded.

MAC algorithm HMAC_SHA256 with a 256-bit key as derived from the key derivation function.

Encryption public key format

The encryption public key and the ephemeralPublicKey returned in Google payloads are formatted with the base64 representation of the key in uncompressed point format. It consists of the following two elements:

  • One magic number that specifies the format (0x04).
  • Two 32-byte large integers that represent the X and Y coordinates in the Elliptic Curve.

This format is described in more detail in "Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)," ANSI X9.62, 1998.

Use OpenSSL to generate a public key

Step 1: Generate a private key

The following example generates an Elliptic Curve private key suitable for use with NIST P-256 and writes it to key.pem:

openssl ecparam -name prime256v1 -genkey -noout -out key.pem

Optional: View the private and public keys

Use the following command to view both the private and public key:

openssl ec -in key.pem -pubout -text -noout

The command produces an output similar to the following:

read EC key
Private-Key: (256 bit)
priv:
    08:f4:ae:16:be:22:48:86:90:a6:b8:e3:72:11:cf:
    c8:3b:b6:35:71:5e:d2:f0:c1:a1:3a:4f:91:86:8a:
    f5:d7
pub:
    04:e7:68:5c:ff:bd:02:ae:3b:dd:29:c6:c2:0d:c9:
    53:56:a2:36:9b:1d:f6:f1:f6:a2:09:ea:e0:fb:43:
    b6:52:c6:6b:72:a3:f1:33:df:fa:36:90:34:fc:83:
    4a:48:77:25:48:62:4b:42:b2:ae:b9:56:84:08:0d:
    64:a1:d8:17:66
ASN1 OID: prime256v1

Step 2: Generate a base64-encoded public key

The private and public key that's generated in the previous optional step example is hexadecimal-encoded. To get a base64-encoded public key in uncompressed point format, use the following command:

openssl ec -in key.pem -pubout -text -noout 2> /dev/null | grep "pub:" -A5 | sed 1d | xxd -r -p | base64 | paste -sd "\0" - | tr -d '\n\r ' > publicKey.txt

The command produces a publicKey.txt file whose content, the base64 version of the key in uncompressed point format, resembles the following:

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

The file content must not have extra empty spaces or carriage returns. To verify this, run the following command in Linux or MacOS:

od -bc publicKey.txt

Step 3: Generate a base64-encoded private key in PKCS #8 format

The Tink library expects your private key to be base64-encoded in PKCS #8 format. Use the following command to generate the private key in this format from the private key generated in the first step:

openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -

The command produces an output similar to the following:

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

How to decrypt the payment method token

Follow these steps to decrypt the token:

  1. Use your private key and the given ephemeralPublicKey to derive a 512-bit long shared key that uses ECIES-KEM. Use the following parameters:
    • Elliptic curve: NIST P-256, also known in OpenSSL as prime256v1.
    • CheckMode, OldCofactorMode, SingleHashMode, and CofactorMode are 0.
    • Encoding function: Uncompressed point format.
    • Key derivation function: HKDFwithSHA256, as described in RFC 5869, with the following parameter:
      • Salt must not be provided. Per the RFC, this must be equivalent to a salt of 32 zeroed bytes.
  2. Split the generated key into two 256-bit-long keys: symmetricEncryptionKey and macKey.
  3. Verify that the tag field is a valid MAC for encryptedMessage.

    To generate the expected MAC, use HMAC (RFC 5869) with hash function SHA256 and the macKey obtained in Step 2.

  4. Decrypt encryptedMessage with the use of AES-256-CTR mode, and with the following:

    • A zero IV.
    • Not padded.
    • The symmetricEncryptionKey derived in Step 2.

Key management

Merchant encryption keys

Merchants generate a public key as per the specifications outlined in Encryption scheme specification.

Google root signing keys

Google publishes the set of currently valid root signing public keys that are fetchable from a public URL. The keys are valid for as long as the HTTP cache headers that are returned by the URL indicate. They're cached until they expire, which is determined by the keyExpiration field. We recommend that when a fetch expires, fetch the keys from the public URL again to receive the current list of valid keys.

Exception for ECv2 protocol: If you can't fetch the keys from Google at runtime, fetch the keys.json from our production URL, save it into your system, and periodically refresh manually. Under normal circumstances, Google issues a new root signing key for ECv2 five years before the key with the longest expiration date expires. In case of key compromises, Google notifies all merchants through the contact information provided in the self-service portal in order to request a quicker reload of keys.json. To make sure you don't miss the regular rotation, we recommend that merchants who choose to save Google keys in the contents of keys.json refresh annually as part of their own annual key rotation.

The keys provided through the public URL are mapped in the following format:

{
  "keys": [
    {
      "keyValue": "encoded public key",
      "protocolVersion": "ECv2"
      "keyExpiration":"2000000000000"
    },
    {
      "keyValue": "encoded public key",
      "protocolVersion": "ECv2"
      "keyExpiration":"3000000000000"
    }
  ]
}

The keyValue is a base64, not wrapped or padded, version of the key encoded in ASN.1 type SubjectPublicKeyInfo defined in the X.509 standard. In Java, the referred ASN.1 encoding is represented by the X509EncodedKeySpec class. It can be obtained with ECPublicKey.getEncoded().

URLs for both test and production environments are provided by the following links:

Key rotation

If you decrypt a payment method token directly on your servers with direct integration, then you must rotate the keys annually.

Complete the following steps to rotate encryption keys:

  1. Use OpenSSL to generate a new key pair.
  2. Open the Google Pay & Wallet Console while signed in with the Google Account that you previously used to sign up as a developer for Google Pay.
  3. In the Google Pay API tab, under the Direct integration pane, click Manage next to your existing public key. Click Add another key.
  4. Select the Public encryption key text input field and add your newly generated public key base64-encoded in uncompressed point format.
  5. Click Save encryption keys.
  6. To ensure a seamless key rotation, support both the new and old private keys decryption while you transition the keys.

    If you use the Tink library to decrypt the token, use the following Java code to support multiple private keys:

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
            .addRecipientPrivateKey(newPrivateKey)
            .addRecipientPrivateKey(oldPrivateKey);

    Make sure that the code for decryption is deployed to production and that you monitor successful decryptions.

  7. Change the public key used in your code.

    Replace the value of the publicKey attribute in the PaymentMethodTokenizationSpecification parameters property:

    const tokenizationSpecification = {
      "type": "DIRECT",
      "parameters": {
        "protocolVersion": "ECv2",
        "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y="
      }
    }
  8. Deploy the code from step 4 to production. Once the code is deployed, encryption and decryption transactions use the new key pairs.
  9. Confirm that the old public key is no longer used to encrypt any transactions.

  10. Remove the old private key.
  11. Open the Google Pay & Wallet Console while signed in with the Google Account that you previously used to sign up as a developer with Google Pay.
  12. In the Google Pay API tab, under the Direct integration pane, click Manage next to your existing public key. Click Delete next to your old public key and click Save encryption keys.

Google uses the key specified in the publicKey property within PaymentMethodTokenizationSpecification parameters object, as shown in the following example:

{
  "protocolVersion": "ECv2",
  "publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}

Use the Tink library to manage the encrypted response

To perform signature verification and message decryption, use the Tink paymentmethodtoken library. This library is only available in Java. To use it, complete the following steps:

  1. In your pom.xml, add the Tink paymentmethodtoken app as a dependency:

    <dependencies>
      <!-- other dependencies ... -->
      <dependency>
        <groupId>com.google.crypto.tink</groupId>
        <artifactId>apps-paymentmethodtoken</artifactId>
        <version>1.9.1</version>  <!-- or latest version -->
      </dependency>
    </dependencies>
  2. At server startup, prefetch the Google signing keys to make the key available in memory. This prevents a user view of any network latency while the decryption process fetches the keys.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. Decrypt the message with the following code, which assumes paymentMethodToken is stored in the encryptedMessage variable, and replace the sections in bold in accordance with your scenario.

    For environment tests, replace INSTANCE_PRODUCTION with INSTANCE_TEST and [YOUR MERCHANT ID] with 12345678901234567890.

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
        .fetchSenderVerifyingKeysWith(
            GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION)
        .recipientId("merchant:[YOUR MERCHANT ID]")
        // This guide applies only to protocolVersion = ECv2
        .protocolVersion("ECv2")
        // Multiple private keys can be added to support graceful
        // key rotations.
        .addRecipientPrivateKey(PrivateKey1)
        .addRecipientPrivateKey(PrivateKey2)
        .build()
        .unseal(encryptedMessage);
  4. Replace PrivateKey1 with the appropriate private key value that associates with the registered public key value with Google from Prepare your keys and register with Google. You can add multiple other private key values later on when it requires you to Rotate keys with Google. The variables can be either a base64-encoded PKCS8 string or an ECPrivateKey object. For more information on how to produce a base64-encoded PKCS8 private key, see Prepare your keys and register with Google.

  5. If you aren't able to call a Google server every time you decrypt keys, decrypt with the following code and replace the sections in bold in accordance with your scenario.

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
        .addSenderVerifyingKey("ECv2 key fetched from test or production url")
        .recipientId("merchant:[YOUR MERCHANT ID]")
        // This guide applies only to protocolVersion = ECv2
        .protocolVersion("ECv2")
        // Multiple private keys can be added to support graceful
        // key rotations.
        .addRecipientPrivateKey(PrivateKey1)
        .addRecipientPrivateKey(PrivateKey2)
        .build()
        .unseal(encryptedMessage);

    The current key in the production environment is valid until 04/14/2038 under normal circumstances except key compromises. In case of key compromises, Google notifies all merchants through the contact information provided in the self-service portal in order to request a quicker reload of keys.json.

    The code snippet handles the following security details so you can focus on the consumption of the payload:

    • Google signing keys fetched and cached in memory
    • Signature verification
    • Decryption