const crypto = require('crypto'); const PREFIX = 'enc:v1'; function getSecretKey() { const source = process.env.PAYMENT_CREDENTIAL_SECRET || process.env.JWT_SECRET || process.env.SNIPPE_API_KEY || 'development-only-payment-credential-secret'; return crypto.createHash('sha256').update(String(source)).digest(); } function encryptSecret(value) { if (!value) return null; const text = String(value); if (text.startsWith(`${PREFIX}:`)) return text; const iv = crypto.randomBytes(12); const cipher = crypto.createCipheriv('aes-256-gcm', getSecretKey(), iv); const ciphertext = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]); const tag = cipher.getAuthTag(); return [ PREFIX, iv.toString('base64url'), tag.toString('base64url'), ciphertext.toString('base64url'), ].join(':'); } function decryptSecret(value) { if (!value) return null; const text = String(value); if (!text.startsWith(`${PREFIX}:`)) return text; const parts = text.split(':'); if (parts.length !== 5) { throw new Error('Invalid encrypted secret format'); } const [, , ivValue, tagValue, ciphertextValue] = parts; const decipher = crypto.createDecipheriv( 'aes-256-gcm', getSecretKey(), Buffer.from(ivValue, 'base64url') ); decipher.setAuthTag(Buffer.from(tagValue, 'base64url')); return Buffer.concat([ decipher.update(Buffer.from(ciphertextValue, 'base64url')), decipher.final(), ]).toString('utf8'); } function maskSecret(value) { const secret = decryptSecret(value); if (!secret) return null; if (secret.length <= 8) return '********'; return `${secret.slice(0, 4)}...${secret.slice(-4)}`; } module.exports = { decryptSecret, encryptSecret, maskSecret, };