Apple pay processing payment fail

Hey, I am trying to implement the apple pay process pay backend service, I have checked everything and somehow it fails. I only have 1 certificate for merchant and 1 for the apple pay process, I have the private keys and try to run this following code that fails -

import crypto from 'crypto'; import fs from 'fs'; import forge from 'node-forge';

const MERCHANT_ID_FIELD_OID = '1.2.840.113635.100.6.32';

function decryptedToken() { const token = "<token data here>"; const ephemeralPublicKey = "<encrypted ephemeral public key here>"; const encryptedData = "<encrypted data here>"; //=================================== // Import certs //=================================== const epk = Buffer.from(ephemeralPublicKey, 'base64'); const merchantCert = fs.readFileSync('merchant_full.pem', 'utf8') const paymentProcessorCert = fs.readFileSync("apple_pay_private.pem"); //===================================

let symmetricKey = '';
try {
	symmetricKey = restoreSymmetricKey(epk, merchantCert, paymentProcessorCert);
} catch (err) {
	throw new Error(`Restore symmetric key failed: ${err.message}`);
}
try {
//-----------------------------------
	// Use the symmetric key to decrypt the value of the data key
//-----------------------------------
	const decrypted = JSON.parse(decryptCiphertextFunc(symmetricKey, encryptedData));
    console.log("Decrypted Token:", decrypted);
// const preppedToken = prepTabaPayToken(token, decrypted)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Send decrypted token back to frontend
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// res.send(preppedToken);
} catch (err) {
	throw new Error(`Decrypt cipher data failed: ${err.message}`);
}

}

// extractMerchantID - const extractMerchantID = (merchantCert) => { //=================================== // Extract merchant identification from public key certificate //=================================== try { const info = forge.pki.certificateFromPem(merchantCert); const result = info['extensions'].filter(d => d.id === MERCHANT_ID_FIELD_OID); //----------------------------------- // Return //----------------------------------- return result[0].value.toString().substring(2); } catch (err) { throw new Error(Unable to extract merchant ID from certificate: ${err}); } }

// generateSharedSecret - const generateSharedSecret = (merchantPrivateKey, ephemeralPublicKey) => { //=================================== // Use private key from payment processing certificate and the ephemeral public key to generate // the shared secret using Elliptic Curve Diffie*Hellman (ECDH) //===================================

const privateKey =  crypto.createPrivateKey({
    key: merchantPrivateKey,
    format: "pem",
    type: "sec1", // because it's "EC PRIVATE KEY"
});
const publicKey = crypto.createPublicKey({
        key: ephemeralPublicKey,
        format: 'der',
        type: 'spki'
    });

//----------------------------------- // Return //----------------------------------- return crypto.diffieHellman({privateKey,publicKey: publicKey,}); //----------------------------------- }

// getSymmetricKey - const getSymmetricKey = (merchantId, sharedSecret) => { //=================================== // Get KDF_Info as defined from Apple Pay documentation //=================================== const KDF_ALGORITHM = '\x0didaes256GCM'; const KDF_PARTY_V = Buffer.from(merchantId, 'hex').toString('binary'); const KDF_PARTY_U = 'Apple'; const KDF_INFO = KDF_ALGORITHM + KDF_PARTY_U + KDF_PARTY_V; //----------------------------------- // Create hash //----------------------------------- const hash = crypto.createHash('sha256'); hash.update(Buffer.from('000000', 'hex')); hash.update(Buffer.from('01', 'hex')); hash.update(Buffer.from(sharedSecret, 'hex')); hash.update(KDF_INFO, 'binary'); //----------------------------------- // Return //----------------------------------- return hash.digest('hex'); //----------------------------------- }

// restoreSymmetricKey - const restoreSymmetricKey = (ephemeralPublicKey, merchantCert, paymentProcessorCert) => { //=================================== // 3.a Use the payment processor private key and the ephemeral public key, to generate the shared secret //=================================== const sharedSecret = generateSharedSecret(paymentProcessorCert, ephemeralPublicKey);

//----------------------------------- // 3.b Use the merchant identifier of the public key certificate and the shared secret, to derive the symmetric key //----------------------------------- const merchantId = extractMerchantID(merchantCert); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Return //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ console.log("Merchant ID:", merchantId); console.log("Shared Secret (hex):", sharedSecret); return getSymmetricKey(merchantId, sharedSecret); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ }

// decryptCiphertextFunc - const decryptCiphertextFunc = (symmetricKey, encryptedData) => { console.log("🔑 Decrypting Ciphertext with Symmetric Key:", symmetricKey); //=================================== // Get symmetric key and initialization vector //=================================== const buf = Buffer.from(encryptedData, 'base64'); const SYMMETRIC_KEY = Buffer.from(symmetricKey, 'hex'); const IV = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // Initialization vector of 16 null bytes const CIPHERTEXT = buf.slice(0, -16); //----------------------------------- // Create and return a Decipher object that uses the given algorithm and password (key) //----------------------------------- const decipher = crypto.createDecipheriv("aes-256-gcm", SYMMETRIC_KEY, IV); const tag = buf.slice(-16, buf.length); decipher.setAuthTag(tag); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Load encrypted token into Decipher object //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ let decrypted = decipher.update(CIPHERTEXT); console.log("🔑 Decrypted Data"); decrypted += decipher.final(); //::::::::::::::::::::::::::::::::::: // Return //::::::::::::::::::::::::::::::::::: return decrypted; //::::::::::::::::::::::::::::::::::: } decryptedToken();

Error: Decrypt cipher data failed: Unsupported state or unable to authenticate data at decryptedToken (file:///C:/Users/icp25/Desktop/VSC-Apple/Ap_data/test_apple.js:58:9) at file:///C:/Users/icp25/Desktop/VSC-Apple/Ap_data/test_apple.js:176:1 at ModuleJob.run (node:internal/modules/esm/module_job:195:25) at async ModuleLoader.import (node:internal/modules/esm/loader:337:24) at async loadESM (node:internal/process/esm_loader:34:7) at async handleMainPromise (node:internal/modules/run_main:106:12)

Node.js v18.20.8 (venv) PS C:\Users\icp25\Desktop\VSC-Apple\Ap_data>

it fails - decrypted += decipher.final(); and here it not printing the data as a readable data - let decrypted = decipher.update(CIPHERTEXT);

Hi @Idang,

You wrote:

Hey, I am trying to implement the apple pay process pay backend service, I have checked everything and somehow it fails. I only have 1 certificate for merchant and 1 for the apple pay process, I have the private keys and try to run this following code that fails - [...]

Please review the following post so we can gather the proper data from your testing device and server-side implementation:

Gathering Required Information for Troubleshooting Apple Pay on the Web Merchant Issues

https://developer.apple.com/forums/thread/762994

Additionally, common reasons for payment token decryption errors are also listed in the technote below:

TN3176: Troubleshooting Apple Pay payment processing issues: Possible reasons for payment token decryption errors

https://developer.apple.com/documentation/technotes/tn3176-troubleshooting-apple-pay-payment-processing-issues#Possible-reasons-for-payment-token-decryption-errors

If you continue to have issues with your description, please reply to your post with the submitted Feedback ID and all requested information.

Cheers,

Paris X Pinkney |  WWDR | DTS Engineer

Apple pay processing payment fail
 
 
Q