import forge from 'node-forge' export class CPCard { private static readonly _pubKey = '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArBZ1NNjvszen6BNWsgyDUJvDUZDtvR4jKNQtEwW1iW7hqJr0TdD8hgTxw3DfH+Hi/7ZjSNdH5EfChvgVW9wtTxrvUXCOyJndReq7qNMo94lHpoSIVW82dp4rcDB4kU+q+ekh5rj9Oj6EReCTuXr3foLLBVpH0/z1vtgcCfQzsLlGkSTwgLqASTUsuzfI8viVUbxE1a+600hN0uBh/CYKoMnCp/EhxV8g7eUmNsWjZyiUrV8AA/5DgZUCB+jqGQT/Dhc8e21tAkQ3qan/jQ5i/QYocA/4jW3WQAldMLj0PA36kINEbuDKq8qRh25v+k4qyjb7Xp4W2DywmNtG3Q20MQIDAQAB\n-----END PUBLIC KEY-----' private static readonly _keyVersion = '04' private constructor( private _number: string, private _expirationDate: string, private _cvv: string ) { } private static getPublicKey = () => forge.pki.publicKeyFromPem(CPCard._pubKey) as forge.pki.rsa.PublicKey static create = (number: string, expirationDate: string, cvv: string) => new Promise((resolve, reject) => { if (!CPCard.isValidNumber(number)) { return reject('Wrong card number') } if (!CPCard.isValidExpDate(expirationDate)) { return reject('Wrong expiration date') } return resolve(new CPCard(number, expirationDate, cvv)) }) cardCryptogram = (publicId: string) => new Promise ((resolve, _) => { const cardNumber = CPCard.prepareCardNumber(this._number) const shortNumber = cardNumber.substring(0, 6) + cardNumber.substring(cardNumber.length - 4) const exp = this._expirationDate.substring(2, 4) + this._expirationDate.substring(0, 2) const s = `${cardNumber}@${exp}@${this._cvv}@${publicId}` const forgeBytes = CPCard.getPublicKey().encrypt(s, 'RSA-OAEP') const forge64 = '02' + shortNumber + exp + CPCard._keyVersion + forge.util.encode64(forgeBytes) return resolve(forge64) }) static isValidNumber = (inputNum: string): boolean => { let sum = 0 let i: number const number = CPCard.prepareCardNumber(inputNum) if (number.trim().length === 0) { return false } if (number.length % 2 == 0) { for (i = 0; i < number.length; i += 2) { let c = parseInt(number.substring(i, i + 1)) c *= 2 if (c > 9) { c -= 9 } sum += c sum += parseInt(number.substring(i + 1, i + 2)) } } else { for (i = 1; i < number.length; i += 2) { let c = parseInt(number.substring(i, i + 1)) c *= 2 if (c > 9) { c -= 9 } sum += c sum += parseInt(number.substring(i - 1, i)) } sum += parseInt(number.substring(i - 1, i)) } return sum % 10 === 0 } static isValidExpDate = (inputDate: string) => { if (inputDate.length !== 4) { return false } // todo check between 1 and 12 const month = parseInt(inputDate.substr(0, 2)) const year = parseInt(inputDate.substr(2, 2)) + 2000 const dateToCompare = new Date(year, month).getTime() - 1 return dateToCompare > new Date().getTime() } private static prepareCardNumber = (cardNumber: string) => cardNumber.split(/\s/).join('') }