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:
- Fetch the Google root signing keys.
- Verify that the signature of the intermediate signing key is valid by any of the non-expired root signing keys.
- Verify that the intermediate signing key of the payload hasn't expired.
- Verify that the signature of the payload is valid by the intermediate signing key.
- Decrypt the contents of the payload after you verify the signature.
- 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. - 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.
|
Key derivation function | HMAC-based with SHA-256 (
|
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:
- 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
, andCofactorMode
are0
.- 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.
- Split the generated key into two 256-bit-long keys:
symmetricEncryptionKey
andmacKey
. Verify that the
tag
field is a valid MAC forencryptedMessage
.To generate the expected MAC, use HMAC (RFC 5869) with hash function SHA256 and the
macKey
obtained in Step 2.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:
- Test:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json
- Production:
https://payments.developers.google.com/paymentmethodtoken/keys.json
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:
- Use OpenSSL to generate a new key pair.
- Open the Google Pay & Wallet Console while signed in with the Google Account previously used to manage your app with Google Play.
- In the Google Pay API tab, under the Direct integration pane, click Manage next to your existing public key. Click Add another key.
- Select the Public encryption key text input field and add your newly generated public key base64-encoded in uncompressed point format.
- Click Save encryption keys.
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.
Change the public key used in your code.
Replace the value of the
publicKey
attribute in thePaymentMethodTokenizationSpecification
parameters
property:/** * @param publicKey public key retrieved from your server */ private static JSONObject getTokenizationSpecification(String publicKey) { JSONObject tokenizationSpecification = new JSONObject(); tokenizationSpecification.put("type", "DIRECT"); tokenizationSpecification.put( "parameters", new JSONObject() .put("protocolVersion", "ECv2") .put("publicKey", publicKey)); return tokenizationSpecification; }
- Deploy the code from step 4 to production. Once the code is deployed, encryption and decryption transactions use the new key pairs.
Confirm that the old public key is no longer used to encrypt any transactions.
- Remove the old private key.
- 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.
- 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:
In your
pom.xml
, add the Tinkpaymentmethodtoken
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>
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();
Decrypt the message with the following code, which assumes
paymentMethodToken
is stored in theencryptedMessage
variable, and replace the sections in bold in accordance with your scenario.For environment tests, replace
INSTANCE_PRODUCTION
withINSTANCE_TEST
and [YOUR MERCHANT ID] with.
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);
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 anECPrivateKey
object. For more information on how to produce a base64-encoded PKCS8 private key, see Prepare your keys and register with Google.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