PayFacto API - V2.0 - Digital Wallets : Google Pay™ - Online Checkout
PayFacto API - V2.0 - Digital Wallets : Google Pay™ - Online Checkout
In-App
Integrate Google Pay into your Android application and process payments through the PayFacto Payment API v2.0. There are two implementation methods depending on who decrypts the Google Pay token — your server or PayFacto.
Choose an Integration Method
Your server decrypts the Google Pay token and extracts the raw card data (PAN, expiry, cryptogram). You then send the decrypted fields to PayFacto for processing.
✅ Full control over decryption
✅ You own the HSM / key management
⚠️ Requires ECv2 decryption implementation
Your server forwards the raw encrypted Google Pay token to PayFacto. PayFacto handles decryption on its end — your integration is simpler.
✅ Simpler — no decryption required
✅ Pass the token as-is
⚠️ Requires payfacto gateway in Google Pay config
Google Pay type: PAYMENT_GATEWAY
A
Method A — Partner DecryptsGoogle Pay config: type = DIRECT
ℹ️
In your Google Pay API configuration, set the tokenization type to DIRECT. This tells Google Pay to use your own merchant public key for encryption, which your server will decrypt.
JSON — Google Pay tokenization config (DIRECT)
"type": "DIRECT"
A2
Receive the Encrypted Token from Google Pay
ℹ️
After the customer approves the payment, Google Pay returns an encrypted token from paymentMethodData.tokenizationData.token. The outer envelope uses the ECv2 protocol.
JSON — Raw Google Pay token (ECv2)
{
"protocolVersion": "ECv2",
"signedMessage": "...",
"signature": "...",
"intermediateSigningKey": {
"signedKey": "...",
"signatures": ["..."]
}
}Inside the verified signedMessage is a payload containing:
| Field | Description |
|---|
| encryptedMessage | The AES-256-GCM encrypted payment data ciphertext. |
| ephemeralPublicKey | Google's one-time EC public key used in the ECDH key agreement. |
| tag | The authentication tag for the AES-GCM operation. Discard the token if verification fails. |
A3
Decrypt the Token (ECv2)
⚠️
Always verify the Google signature chain before decrypting. This prevents token forgery and replay attacks.
Follow these six steps on your server to produce the plaintext payment data:
1
Verify token authenticity
Validate Google's signature chain. Verify intermediateSigningKey, then signedMessage. Confirm all signing keys trace to Google's trusted root certificate. Do not proceed if verification fails.
2
Load your merchant private key
Retrieve the Elliptic Curve (P‑256) private key that corresponds to the public key you uploaded in the Google Pay Business Console. Store it securely in an HSM or KMS.
3
Perform ECDH key agreement
Use your merchant private key and Google's ephemeralPublicKey from the token to compute a shared secret via Elliptic Curve Diffie-Hellman (ECDH).
4
Derive symmetric keys with HKDF
Run the shared secret through HKDF (SHA‑256) using Google-specified context strings (protocol version, sender/recipient identifiers) to produce an AES‑256 symmetric key and an HMAC key.
5
Decrypt the payment data
Decrypt encryptedMessage using AES‑256‑GCM with the derived AES key and tag as the authentication tag. If authentication fails, discard the token immediately.
6
Parse the decrypted payload
The result is a JSON payload containing PAN, expiry, cryptogram, and ECI indicator. Extract these four fields for the next step.
JSON — Decrypted payload
{
"gatewayMerchantId": "94087155",
"messageExpiration": "1741304144394",
"messageId": "AH2EjtdzTuvrRcGAbH2k9B...",
"paymentMethod": "CARD",
"paymentMethodDetails": {
"expirationYear": 2030,
"expirationMonth": 10,
"pan": "1234567890123456",
"authMethod": "CRYPTOGRAM_3DS",
"eciIndicator": "07",
"cryptogram": "xQBBBBBB12abCDefGh+9Z0AAAA="
}
}Fields to extract from the decrypted payload
| Decrypted field | Maps to | Notes |
|---|
| pan | Card number | Pass as card.number in the charge request. |
| expirationYear | Expiry year (YYYY) | Pass as card.expiry.year. |
| expirationMonth | Expiry month (MM) | Pass as card.expiry.month. Zero-pad to 2 digits. |
| cryptogram | Cryptogram | Must be converted from Base64 to HEX before sending to PayFacto. See below. |
| eciIndicator | ECI indicator | Pass as card.authentication.wallet.eciIndicator. |
A4
Convert the Cryptogram from Base64 to HEX
⚠️
The cryptogram returned by Google Pay is Base64-encoded. PayFacto requires it in hexadecimal (HEX) format. Convert it before building the charge request.
| Encoding | Example value |
|---|
| Base64 (from Google Pay) | xQBBBBBB12abCDefGh+9Z0AAAA= |
| HEX (send to PayFacto) | C50041041041075D9A6C305E9C1FBD6740000000 |
Java — Base64 → HEX conversion
import java.util.HexFormat;
public String getCryptogramHexString(final GooglePayPaymentPlainData plainData) {
final String base64String = plainData.getCryptogram3ds();
final byte[] bytes = decodeBase64(base64String);
return HexFormat.of().formatHex(bytes);
}
A5
Build and Submit the Charge
ℹ️
Use paymentMethod.type: "CARD" with a card.authentication.wallet block. Set wallet.type to GOOGLE_PAY and pass the HEX-encoded cryptogram.
Charge request body — Method A
| Field | Constraint | Description |
|---|
| amount | integer · required | Amount in cents. Example: 5000 = $50.00 |
| invoiceNumber | string · required | Merchant invoice reference. Max 25 chars. |
| capture | boolean · optional | Default: true. Set false for pre-authorization. |
| paymentMethod.type | string · required | CARD |
| paymentMethod.card.number | string · required | PAN extracted from the decrypted Google Pay payload. |
| paymentMethod.card.expiry.month | string · required | Expiry month (MM) from the decrypted payload. |
| paymentMethod.card.expiry.year | string · required | Expiry year (YYYY) from the decrypted payload. |
| paymentMethod.card.authentication.wallet.type | string · required | GOOGLE_PAY |
| paymentMethod.card.authentication.wallet.eciIndicator | string · required | ECI indicator from the decrypted payload. |
| paymentMethod.card.authentication.wallet.cryptogram | string · required | Cryptogram converted to HEX. Do not send the raw Base64 value. |
JSON — POST /charges body (Method A — Partner Decrypts)
{
"amount": "5000",
"invoiceNumber": "123456789012",
"capture": true,
"paymentMethod": {
"type": "CARD",
"card": {
"number": "1234567890123456",
"expiry": {
"month": "08",
"year": "2030"
},
"authentication": {
"wallet": {
"eciIndicator": "05",
"type": "GOOGLE_PAY",
"cryptogram": "C50041041041075D9A6C305E9C1FBD6740000000"
}
}
}
}
}B
Method B — PayFacto DecryptsGoogle Pay config: type = PAYMENT_GATEWAY
ℹ️
In your Google Pay API configuration, set the tokenization type to PAYMENT_GATEWAY and specify payfacto as the gateway. The gatewayMerchantId is your PayFacto merchant account UUID, found in the merchant back office.
JSON — Google Pay tokenization config (PAYMENT_GATEWAY)
{
"tokenizationSpecification": {
"type": "PAYMENT_GATEWAY",
"parameters": {
"gateway": "payfacto",
"gatewayMerchantId": "merchant_123"
}
}
}| Parameter | Description |
|---|
| gateway | Must be exactly payfacto (lowercase). This tells Google Pay which gateway will decrypt the token. |
| gatewayMerchantId | Your PayFacto merchant account UUID. Found in the PayFacto merchant back office. |
B2
Receive and Forward the Token
ℹ️
With PAYMENT_GATEWAY mode, Google returns a complete encrypted token payload. Your server does not need to decrypt it. Extract the full value of paymentMethodData.tokenizationData.token as a JSON string and pass it directly to PayFacto as paymentData.
⚠️
Pass the token value exactly as received — as a JSON string. Do not parse, modify, or re-serialize it. PayFacto will handle all decryption internally.
B3
Build and Submit the Charge
ℹ️
Use paymentMethod.type: "CARD" with a card.authentication.wallet block. Set wallet.type to GOOGLE_PAY and pass the raw token JSON string as wallet.paymentData. No card number, expiry, or cryptogram are required.
Charge request body — Method B
| Field | Constraint | Description |
|---|
| amount | integer · required | Amount in cents. |
| invoiceNumber | string · required | Merchant invoice reference. Max 25 chars. |
| capture | boolean · optional | Default: true. Set false for pre-authorization. |
| paymentMethod.type | string · required | CARD |
| paymentMethod.card.authentication.wallet.type | string · required | GOOGLE_PAY |
| paymentMethod.card.authentication.wallet.paymentData | string · required | The complete Google Pay token JSON string from paymentMethodData.tokenizationData.token. Pass as-is — do not parse. |
JSON — POST /charges body (Method B — PayFacto Decrypts)
{
"amount": "10000",
"invoiceNumber": "581113481611",
"capture": true,
"metadata": {
"device_type": "android"
},
"paymentMethod": {
"type": "CARD",
"card": {
"authentication": {
"wallet": {
"type": "GOOGLE_PAY",
"paymentData": "{\"signature\":\"MEUCIHcdC0B4TKe...\",\"protocolVersion\":\"ECv2\",\"signedMessage\":\"{\\\"encryptedMessage\\\":\\\"kQl0kOsKNhE3FQ...\\\"}\"}"
}
}
}
}
}