Last active
September 15, 2020 14:23
-
-
Save liamkernighan/84521ff6e4c0bcff8071f6f612bcca22 to your computer and use it in GitHub Desktop.
Криптограмма CloudPayments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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<CPCard>((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<string> ((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('') | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment