Skip to content

Instantly share code, notes, and snippets.

@liamkernighan
Last active September 15, 2020 14:23
Show Gist options
  • Save liamkernighan/84521ff6e4c0bcff8071f6f612bcca22 to your computer and use it in GitHub Desktop.
Save liamkernighan/84521ff6e4c0bcff8071f6f612bcca22 to your computer and use it in GitHub Desktop.
Криптограмма CloudPayments
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