0

I'm trying to integrate Google Pay in Direct Integration mode. I'm having trouble decrypting the payment data once received.

public static String decryptGooglePayTokenWithTink(
            String encryptedMessage,
            String privateKeyBase64,
            String merchantId,
            boolean isTestEnvironment
    ) throws Exception {
        // Per Google documentation:
        // - For TEST: Use "merchant:[MERCHANT ID]" format
        // - For PRODUCTION: Use "merchant:[MERCHANT ID]" format
        // If merchant ID is not configured, use empty string
        String recipientId = (merchantId != null && !merchantId.trim().isEmpty()) 
                ? "merchant:" + merchantId 
                : "";
        
        try {
            // Build recipient using Tink library exactly as per Google documentation
            PaymentMethodTokenRecipient.Builder builder = new PaymentMethodTokenRecipient.Builder();
            
            // Use appropriate Google signing keys manager based on environment
            // Keys are prefetched at startup for performance
            if (isTestEnvironment) {
                builder.fetchSenderVerifyingKeysWith(GooglePaymentsPublicKeysManager.INSTANCE_TEST);
            } else {
                builder.fetchSenderVerifyingKeysWith(GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION);
            }
            
            // Set recipient ID per Google documentation: "merchant:[MERCHANT ID]"
            builder.recipientId(recipientId);
            
            // Set protocol version (ECv2 only)
            builder.protocolVersion("ECv2");
            
            // Add private key as Base64-encoded PKCS8 string
            // Can add multiple keys for graceful key rotation
            builder.addRecipientPrivateKey(privateKeyBase64);
            
            // Build and unseal (decrypt and verify signatures)
            PaymentMethodTokenRecipient recipient = builder.build();
            return recipient.unseal(encryptedMessage);
        } catch (Exception e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }
    }

This fails. I have added my Public Key on https://pay.google.com/ under the Direct Integration. And I'm trying on TEST mode. The unseal method throws an error.

Error Log:

Caused by: javax.crypto.AEADBadTagException: Tag mismatch! at java.base/com.sun.crypto.provider.GaloisCounterMode$GCMDecrypt.doFinal(GaloisCounterMode.java:1395) at java.base/com.sun.crypto.provider.GaloisCounterMode.engineDoFinal(GaloisCounterMode.java:406) at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2205) at io.apptizer.payments.util.GooglePayECv2Util.decryptGooglePayPayload(GooglePayECv2Util.java:231) ... 73 common frames omitted

What could be causing the issue?

Edit: On the Google Pay Console, my added Public Key is on "Inactive" status. I don't see any option to bring it to Active status.

1 Answer 1

0

eah, that “Tag mismatch!” error usually means your private key doesn’t match the public key you uploaded—or it’s in the wrong format.

Quick checklist:

Public key stuck as “Inactive”? That’s normal in test mode until Google sees a real test payment using that key. Just make sure you’re using the test card (4111 1111 1111 1111) and test environment flags in your frontend.

Private key must be PKCS#8 DER in Base64—not PEM, not PKCS#1. Convert it if needed:

bash

openssl pkcs8 -topk8 -nocrypt -in key.pem -outform der | base64

If you didn’t set a Merchant ID in the Google Pay console, use an empty string for recipientId, not "merchant:".

Fix those and it’ll likely start working.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.