La API de Google Pay devuelve las formas de pago en una carga útil de PaymentMethodToken firmada y encriptada. Las formas de pago que se devuelven son tarjetas que constan de un PAN o tarjetas asignadas con token que constan de un PAN del dispositivo y criptogramas.
La carga útil contiene un campo llamado protocolVersion que le indica al destinatario de la
carga útil qué primitivas criptográficas se usan y el formato esperado.
En esta guía, se proporciona información para generar una clave pública con la que se solicita un token de forma de pago encriptado y firmado por Google, y se detallan los pasos que se deben seguir para verificar y desencriptar el token.
Esta guía solo se aplica a protocolVersion = ECv2.
Como recibes información de tarjetas de pago directamente, asegúrate de que tu app cumpla con el estándar PCI DSS y de que tus servidores tengan la infraestructura necesaria para administrar de forma segura las credenciales de pago del usuario antes de continuar.
En los siguientes pasos, se describe lo que debe hacer un integrador para consumir la carga útil de ECv2 PaymentMethodToken de la API de Google Pay:
- Recupera las claves de firma raíz de Google.
- Verifica que la firma de la clave de firma intermedia sea válida con cualquiera de las claves de firma raíz que no hayan vencido.
- Verifica que la clave de firma intermedia de la carga útil no haya vencido.
- Verifica que la firma de la carga útil sea válida con la clave de firma intermedia.
- Desencripta el contenido de la carga útil después de verificar la firma.
- Verifica que el mensaje no haya vencido. Para ello, debes verificar que la hora actual sea anterior a la del campo
messageExpirationen el contenido desencriptado. - Usa la forma de pago que se encuentra en el contenido desencriptado y realiza el cobro.
El código de muestra de nuestra biblioteca de Tink realiza los pasos del 1 al 6.
Estructura del token de forma de pago
El mensaje que devuelve Google en la respuesta PaymentData es un objeto JSON serializado y codificado en UTF-8 con las claves que se especifican en la siguiente tabla:
| Nombre | Tipo | Descripción |
|---|---|---|
protocolVersion |
String | Identifica el esquema de encriptación o firma con el que se crea el mensaje. Permite que el protocolo evolucione con el tiempo, si es necesario. |
signature |
String | Verifica que el mensaje provenga de Google. Está codificada en Base64 y se creó con ECDSA a través de la clave de firma intermedia. |
intermediateSigningKey |
Objeto | Es un objeto JSON que contiene la clave de firma intermedia de Google. Contiene el elemento
signedKey con keyValue, keyExpiration y
signatures. Se serializa para simplificar el proceso de verificación de la firma de la clave de firma intermedia. |
signedMessage |
String | Es un objeto JSON serializado como una cadena segura para HTML que contiene
encryptedMessage, ephemeralPublicKey y tag. Se serializa para simplificar el proceso de verificación de firmas. |
Ejemplo
A continuación, se muestra una respuesta de token de forma de pago en 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\"}" }
Clave de firma intermedia
El intermediateSigningKey es un objeto JSON serializado y codificado en UTF-8 que contiene
los siguientes valores:
| Nombre | Tipo | Descripción |
|---|---|---|
signedKey |
String | Un mensaje codificado en Base64 que contiene la descripción del pago de la clave. |
signatures |
String | Verifica que la clave de firma intermedia provenga de Google. Está codificada en Base64 y se creó con ECDSA. |
Clave firmada
El signedKey es un objeto JSON serializado y codificado en UTF-8 que contiene los
siguientes valores:
| Nombre | Tipo | Descripción |
|---|---|---|
keyValue |
String | Es una versión en Base64 de la clave codificada en el tipo ASN.1. SubjectPublicKeyInfo se define en el estándar X.509. |
keyExpiration |
String | Es la fecha y hora en que vence la clave intermedia, en milisegundos UTC desde la época. Los integradores rechazan cualquier clave que haya vencido. |
Mensaje firmado
El signedMessage es un objeto JSON serializado y codificado en UTF-8 que contiene los
siguientes valores:
| Nombre | Tipo | Descripción |
|---|---|---|
encryptedMessage |
String | Es un mensaje encriptado codificado en Base64 que contiene información de pago y algunos campos de seguridad adicionales. |
ephemeralPublicKey |
String | Una clave pública efímera codificada en Base64 asociada con la clave privada para encriptar el mensaje en formato de punto sin comprimir. Para obtener más información, consulta Formato de clave pública de encriptación. |
tag |
String | Es una MAC codificada en Base64 de encryptedMessage. |
Mensaje encriptado
El encryptedMessage desencriptado es un objeto JSON serializado y codificado en UTF-8. El JSON contiene dos niveles. El nivel exterior contiene metadatos y campos incluidos por seguridad, mientras que el nivel interior es otro objeto JSON que representa la credencial de pago real.
Para obtener más detalles sobre encryptedMessage, consulta las siguientes tablas y
ejemplos de objetos JSON:
| Nombre | Tipo | Descripción |
|---|---|---|
messageExpiration |
String | Es la fecha y hora en que vence el mensaje, expresada en milisegundos UTC desde la época. Los integradores deben rechazar cualquier mensaje que haya vencido. |
messageId |
String | Es un ID único que identifica el mensaje en caso de que deba revocarse o ubicarse en un momento posterior. |
paymentMethod |
String | Es el tipo de credencial de pago.
Actualmente, solo se admite CARD.
|
paymentMethodDetails |
Objeto | La credencial de pago en sí El formato de este objeto se determina por el
paymentMethod y se describe en las siguientes tablas. |
Tarjeta
Las siguientes propiedades conforman una credencial de pago para la forma de pago CARD:
| Nombre | Tipo | Descripción |
|---|---|---|
pan |
String | Es el número de cuenta personal al que se le cobró. Esta cadena solo contiene dígitos. |
expirationMonth |
Número | Es el mes de vencimiento de la tarjeta, en el que 1 representa enero, 2 representa febrero, etcétera. |
expirationYear |
Número | Es el año de vencimiento de la tarjeta, con formato de cuatro dígitos, p. ej., 2020. |
authMethod |
String | Es el método de autenticación de la transacción con tarjeta. |
merchantTokenId |
String | Es el ID del token de comercio de la transacción con tarjeta. Este campo opcional solo se devuelve si se cumplen todas las condiciones que se indican a continuación:
|
PAN_ONLY
El siguiente fragmento de JSON es un ejemplo del encryptedMessage completo para un
CARD paymentMethod
con un 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
Un CARD autenticado con el uso de un criptograma de 3-D Secure,
CRYPTOGRAM_3DS authMethod. Incluye los siguientes campos adicionales:
| Nombre | Tipo | Descripción |
|---|---|---|
cryptogram |
String | Un criptograma de 3-D Secure. |
eciIndicator |
String | Esta cadena no siempre está presente. Solo se devuelve para las transacciones de tokens de dispositivos autenticados en Android (CRYPTOGRAM_3DS). Este valor se debe pasar por el flujo de procesamiento de pagos. |
El siguiente fragmento de JSON es un ejemplo del encryptedMessage completo para un
CARD paymentMethod con un
CRYPTOGRAM_3DS authMethod:
{
"paymentMethod": "CARD",
"paymentMethodDetails": {
"authMethod": "CRYPTOGRAM_3DS",
"pan": "1111222233334444",
"expirationMonth": 10,
"expirationYear": 2025,
"cryptogram": "AAAAAA...",
"eciIndicator": "eci indicator",
"merchantTokenId": "merchant-token-id",
},
"messageId": "some-message-id",
"messageExpiration": "1759309000000"
}eciIndicator
La red de tarjetas puede proporcionar el eciIndicator para las transacciones de tokens de dispositivos autenticados (CRYPTOGRAM_3DS).
Debes pasar el valor eciIndicator en la transacción de autorización sin que se altere ni se codifique de forma fija; de lo contrario, la transacción fallará. En la siguiente tabla, se detallan los valores de eciIndicator.
| Valor de eciIndicator | Red de tarjetas | Parte responsable | authMethod |
|---|---|---|---|
""(empty) |
Mastercard | Comercio/adquirente | CRYPTOGRAM_3DS |
02 |
Mastercard | Entidad emisora de la tarjeta | CRYPTOGRAM_3DS |
06 |
Mastercard | Comercio/adquirente | CRYPTOGRAM_3DS |
05 |
Visa | Entidad emisora de la tarjeta | CRYPTOGRAM_3DS |
07 |
Visa | Comercio/adquirente | CRYPTOGRAM_3DS |
""(empty) |
Otras redes | Comercio/adquirente | CRYPTOGRAM_3DS |
No se devolverán otros valores de ECI para VISA y Mastercard que no estén presentes en esta tabla.
Verificación de firmas
Para verificar las firmas, que incluyen la clave intermedia y las firmas de mensajes, se requieren los siguientes elementos:
- Es el algoritmo que se usa para crear la firma.
- Es la cadena de bytes que se usa para crear la firma.
- La clave pública que corresponde a la privada que se usó para crear la firma
- La firma en sí
El algoritmo de firma
Google usa el algoritmo de firma digital de curva elíptica (ECDSA) para firmar los mensajes con los siguientes parámetros: ECDSA sobre NIST P-256 con SHA-256 como la función hash, como se define en FIPS 186-4.
La firma
La firma se incluye en el nivel más externo del mensaje. Está codificada con base64 en formato de bytes ASN.1. Para obtener más información sobre ASN.1, consulta el Apéndice A de las herramientas de IETF. La firma consta de los números enteros r y s de ECDSA. Para obtener más información, consulta Algoritmo de generación de firmas.
A continuación, se muestra un ejemplo del formato de bytes ASN.1 especificado, que es el formato estándar que producen las implementaciones de ECDSA de la extensión de criptografía de Java (JCE).
ECDSA-Sig-Value :: = SEQUENCE {
r INTEGER,
s INTEGER
}Cómo construir la cadena de bytes para la firma de la clave de firma intermedia
Para validar la firma de la clave de firma intermedia en el token de forma de pago de muestra, construye
el signedStringForIntermediateSigningKeySignature con la siguiente fórmula:
signedStringForIntermediateSigningKeySignature = length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key
La notación "||" significa concatenar. Cada componente (sender_id,
protocolVersion y signedKey) debe tener codificación UTF-8. El signedKey debe ser la cadena de intermediateSigningKey.signedKey.
La longitud en bytes de cada componente es de 4 bytes en formato little-endian.
Ejemplo
En este ejemplo, se usa el siguiente token de forma de pago de muestra:
{
"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\"}"
}El sender_id siempre es Google y el protocol_version es
ECv2.
Si el sender_id es Google, el signedString aparece como
se muestra en el siguiente ejemplo:
signedStringForIntermediateSigningKeySignature =
\x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}Cómo verificar la firma en signedStringForIntermediateSigningKeySignature
El algoritmo de verificación de ECDSA estándar se usa cuando se ensambla la cadena firmada para la firma de la clave de firma intermedia. Para el protocolo ECv2, debes iterar todas las firmas en intermediateSigningKey.signatures y tratar de validar cada una con las claves de firma de Google que no hayan vencido en keys.json. Si funciona al menos una validación de firma, considera que la verificación se completó. Usa el
intermediateSigningKey.signedKey.keyValue más adelante para verificar el
signedStringForMessageSignature. Google recomienda que uses una biblioteca criptográfica existente en lugar de tu propio código de verificación.
Cómo construir la cadena de bytes para la firma del mensaje
Para validar la firma en el token de forma de pago de muestra, construye el
signedStringForMessageSignature con la siguiente fórmula:
signedStringForMessageSignature = length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage
La notación "||" significa concatenar. Cada componente (sender_id,
recipient_id, protocolVersion, signedMessage) debe estar codificado
en UTF-8. La longitud en bytes de cada componente es de 4 bytes en formato little-endian. Cuando construyas
la cadena de bytes, no analices ni modifiques signedMessage. Por ejemplo, no reemplaces
\u003d por el carácter =.
Ejemplo
El siguiente ejemplo es un token de forma de pago de muestra:
{
"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\"}"
}El sender_id siempre es Google y el recipient_id es
merchant:merchantId. El merchantId coincide con el valor que se encuentra en la consola de Google Pay y la Billetera de Google para los comercios con acceso a producción.
Si el sender_id es Google y el recipient_id
es merchant:12345, el signedString aparece como se muestra en el siguiente
ejemplo:
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"}Cómo verificar la firma en signedStringForMessageSignature
El algoritmo de verificación de ECDSA estándar se usa cuando se ensambla la cadena firmada. La intermediateSigningKey.signedKey.keyValue verificada en el paso anterior se usa para verificar la signedMessage. Google recomienda que uses una biblioteca criptográfica existente en lugar de tu propio código de verificación.
Especificación del esquema de encriptación
Google usa el esquema de encriptación integrada de curva elíptica (ECIES) para proteger el token de la forma de pago que se muestra en la respuesta de la API de Google Pay. El esquema de encriptación usa los siguientes parámetros:
| Parámetro | Definición |
|---|---|
| Método de encapsulamiento de claves | ECIES-KEM, como se define en ISO 18033-2.
|
| Función de derivación de claves | Basado en HMAC con SHA-256 (
|
| Algoritmo de encriptación simétrica |
DEM2, como se define en ISO 18033-2 Algoritmo de encriptación: AES-256-CTR con IV cero y sin relleno. |
| Algoritmo de MAC | HMAC_SHA256 con una clave de 256 bits, como se deriva de la función de derivación de claves. |
Formato de la clave pública de encriptación
La clave pública de encriptación y el ephemeralPublicKey que se devuelven en las cargas útiles de Google tienen el formato de la representación en Base64 de la clave en formato de punto sin comprimir. Consta de los siguientes dos elementos:
- Un número mágico que especifica el formato (0x04)
- Dos números enteros grandes de 32 bytes que representan las coordenadas X e Y en la curva elíptica.
Este formato se describe con más detalle en "Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)", ANSI X9.62, 1998.
Usa OpenSSL para generar una clave pública
Paso 1: Genera una clave privada
En el siguiente ejemplo, se genera una clave privada de curva elíptica adecuada para usar con NIST P-256
y se escribe en key.pem:
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
Opcional: Visualiza las claves públicas y privadas
Usa el siguiente comando para ver la clave privada y la pública:
openssl ec -in key.pem -pubout -text -noout
El comando produce un resultado similar al siguiente:
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
Paso 2: Genera una clave pública codificada en Base64
La clave privada y pública que se genera en el paso opcional anterior está codificada en formato hexadecimal. Para obtener una clave pública codificada en Base64 en formato de punto sin comprimir, usa el siguiente comando:
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
El comando produce un archivo publicKey.txt cuyo contenido, la versión en Base64 de la
clave en formato de punto sin comprimir, se parece al siguiente:
BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=
El contenido del archivo no debe tener espacios vacíos ni retornos de carro adicionales. Para verificar que sea así, ejecuta el siguiente comando en Linux o macOS:
od -bc publicKey.txt
Paso 3: Genera una clave privada codificada en Base64 en formato PKCS #8
La biblioteca de Tink espera que tu clave privada esté codificada en Base64 en formato PKCS #8. Usa el siguiente comando para generar la clave privada en este formato a partir de la clave privada generada en el primer paso:
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
El comando produce un resultado similar al siguiente:
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
Cómo desencriptar el token de la forma de pago
Sigue estos pasos para desencriptar el token:
- Usa tu clave privada y el
ephemeralPublicKeyproporcionado para derivar una clave compartida de 512 bits que use ECIES-KEM. Usa los siguientes parámetros: - Curva elíptica: NIST P-256, también conocida en OpenSSL como prime256v1.
CheckMode,OldCofactorMode,SingleHashModeyCofactorModeson0.- Función de codificación: formato de punto sin comprimir.
- Función de derivación de claves: HKDFwithSHA256, como se describe en RFC 5869, con el siguiente parámetro:
- No se debe proporcionar la sal. Según el RFC, esto debe ser equivalente a una sal de 32 bytes con valor cero.
- Divide la clave generada en dos claves de 256 bits de longitud:
symmetricEncryptionKeyymacKey. Verifica que el campo
tagsea un MAC válido paraencryptedMessage.Para generar el MAC esperado, usa HMAC (RFC 5869) con la función hash SHA256 y el
macKeyobtenido en el paso 2.Desencripta
encryptedMessagecon el modo AES-256-CTR y con lo siguiente:- Un VI de cero.
- Sin relleno.
- El
symmetricEncryptionKeyderivado en el paso 2.
Administración de claves
Claves de encriptación del comercio
Los comercios generan una clave pública según las especificaciones descritas en Especificación del esquema de encriptación.
Claves de firma raíz de Google
Google publica el conjunto de claves públicas de firma raíz válidas actualmente que se pueden recuperar desde una URL pública. Las claves son válidas durante el tiempo que indiquen los encabezados de caché HTTP que muestra la URL. Se almacenan en caché hasta que vencen, lo que se determina con el campo keyExpiration. Te recomendamos que, cuando venza una recuperación, vuelvas a recuperar las claves de la URL pública para recibir la lista actual de claves válidas.
Excepción para el protocolo ECv2: Si no puedes recuperar las claves de Google durante el tiempo de ejecución, recupera la keys.json de nuestra URL de producción, guárdala en tu sistema y actualízala de forma manual periódicamente. En circunstancias normales, Google emite una nueva clave de firma raíz para ECv2 cinco años
antes de que venza la clave con la fecha de vencimiento más larga. En caso de que se vulneren las claves, Google
notifica a todos los comercios a través de la información de contacto proporcionada en el portal de autoservicio para
solicitar una recarga más rápida de keys.json. Para asegurarte de no perderte la
rotación habitual, recomendamos que los comercios que elijan guardar las claves de Google en el contenido de
keys.json las actualicen anualmente como parte de su propia rotación anual de claves.
Las claves proporcionadas a través de la URL pública se asignan en el siguiente formato:
{ "keys": [ { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"2000000000000" }, { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"3000000000000" } ] }
La keyValue es una versión en base64, no unida ni rellenada, de la clave codificada en el tipo ASN.1
SubjectPublicKeyInfo definido en el estándar X.509. En Java, la codificación ASN.1
mencionada se representa con la
clase X509EncodedKeySpec.
Se puede obtener con ECPublicKey.getEncoded().
Las URLs de los entornos de prueba y de producción se proporcionan en los siguientes vínculos:
- Prueba:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json - Producción:
https://payments.developers.google.com/paymentmethodtoken/keys.json
Rotación de claves
Si desencriptas un token de forma de pago directamente en tus servidores con la integración directa, debes rotar las claves anualmente.
Sigue estos pasos para rotar las claves de encriptación:
- Usa OpenSSL para generar un par de claves nuevo.
- Abre la Consola de Google Pay y la Billetera de Google con la Cuenta de Google que usabas para administrar tu app con Google Play.
- En la pestaña API de Google Pay, en el panel Integración directa, haz clic en Administrar junto a tu clave pública existente. Haz clic en Agregar otra clave.
- Selecciona el campo de entrada de texto Clave de encriptación pública y agrega la clave pública que acabas de generar, codificada en Base64 en formato de punto sin comprimir.
- Haz clic en Guardar claves de encriptación.
Para garantizar una rotación de claves sin problemas, admite la desencriptación de las claves privadas nuevas y antiguas mientras realizas la transición de las claves.
Si usas la biblioteca de Tink para desencriptar el token, usa el siguiente código Java para admitir varias claves privadas:
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addRecipientPrivateKey(newPrivateKey) .addRecipientPrivateKey(oldPrivateKey);
Asegúrate de que el código para la desencriptación se implemente en producción y de que supervises las desencriptaciones exitosas.
Cambia la clave pública que se usa en tu código.
Reemplaza el valor del atributo
publicKeyen la propiedadparametersdePaymentMethodTokenizationSpecification:/** * @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; }
- Implementa el código del paso 4 en producción. Una vez que se implementa el código, las transacciones de encriptación y desencriptación usan los pares de claves nuevos.
Confirma que la clave pública anterior ya no se use para encriptar ninguna transacción.
- Quita la clave privada anterior.
- Abre la Consola de Google Pay y la Billetera de Google con la Cuenta de Google que usaste anteriormente para registrarte como desarrollador en Google Pay.
- En la pestaña API de Google Pay, en el panel Integración directa, haz clic en Administrar junto a tu clave pública existente. Haz clic en Borrar junto a tu clave pública anterior y, luego, en Guardar claves de encriptación.
Google usa la clave especificada en la propiedad publicKey dentro del objeto PaymentMethodTokenizationSpecification parameters, como se muestra en el siguiente ejemplo:
{
"protocolVersion": "ECv2",
"publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}Usa la biblioteca de Tink para administrar la respuesta encriptada.
Para realizar la verificación de firmas y la desencriptación de mensajes, usa la biblioteca paymentmethodtoken de Tink. Esta biblioteca solo está disponible en Java. Para usarla, completa los siguientes pasos:
En tu
pom.xml, agrega la app de Tinkpaymentmethodtokencomo una dependencia:<dependencies> <!-- other dependencies ... --> <dependency> <groupId>com.google.crypto.tink</groupId> <artifactId>apps-paymentmethodtoken</artifactId> <version>1.9.1</version> <!-- or latest version --> </dependency> </dependencies>Cuando se inicie el servidor, recupera previamente las claves de firma de Google para que estén disponibles en la memoria. Esto evita que el usuario vea cualquier latencia de red mientras el proceso de desencriptación recupera las claves.
GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
Desencripta el mensaje con el siguiente código, que supone que
paymentMethodTokense almacena en la variableencryptedMessage, y reemplaza las secciones en negrita de acuerdo con tu situación.Para las pruebas que no son de producción, reemplaza
INSTANCE_PRODUCTIONporINSTANCE_TESTy, si tu integración está inactiva o no tiene una clave de encriptación configurada, reemplaza [YOUR MERCHANT ID] por.- Activo
- Tiene habilitada la integración DIRECTA
- Tiene una clave de encriptación configurada
No reemplaces [YOUR MERCHANT ID].
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);
Reemplaza
PrivateKey1por el valor de clave privada adecuado que se asocia con el valor de clave pública registrado con Google de Prepara tus claves y regístrate en Google. Puedes agregar varios valores de clave privada más adelante cuando se te solicite rotar las claves con Google. Las variables pueden ser una cadena PKCS8 codificada en base64 o un objetoECPrivateKey. Para obtener más información sobre cómo producir una clave privada PKCS8 codificada en Base64, consulta Prepara tus claves y regístrate en Google.Si no puedes llamar a un servidor de Google cada vez que desencriptas claves, desencripta con el siguiente código y reemplaza las secciones en negrita según tu situación.
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);
La clave actual en el entorno de producción es válida hasta el 14/4/2038 en circunstancias normales, excepto en caso de vulneración de claves. En caso de que se vulneren las claves, Google notificará a todos los comercios a través de la información de contacto proporcionada en el portal de autoservicio para solicitar una recarga más rápida de
keys.json.El fragmento de código controla los siguientes detalles de seguridad para que puedas enfocarte en el consumo de la carga útil:
- Se recuperaron las claves de firma de Google y se almacenaron en caché en la memoria
- Verificación de firmas
- Desencriptación