Repository URL to install this package:
Version:
0.9.5 ▾
|
/* eslint-disable brace-style */
/**
* @module creditcard
* @todo https://github.com/braintree/card-validator
* @todo https://github.com/braintree/credit-card-type
*
* @todo charAt(0) for TABWL-804 ?
*
* @description in easy checkout page we have used number format
* so adding "3" to accumulate the space between the number seprations
*
* @example $('#skMob_paymentCardCVC, #skMob_creditCardCVC, #skMob_addEditpaymentCardCVC')
* @example `yy/mm/dd`
*/
import { isEmpty, isString, isNumber, EMPTY_REGEXP } from 'exotic'
import { curry2, always } from 'chain-able-boost'
import { test } from './__match'
type CreditCardDataType = string | number
/**
* @todo
*/
const matchSocialSecurityNumber = /^(?!000|666)[0-8][0-9]{2}-?(?!00)[0-9]{2}-?(?!0000)[0-9]{4}$/
/**
* @param {String | Date} x
* @return {Number | String}
*
* @todo inputFieldText;
* @todo if year !== 2;
*/
const getYear = x => (x.split('/')[1] ? x.split('/')[1].length : '')
/**
* @see https://www.freeformatter.com/credit-card-number-generator-validator.html
* @note DOES NOT MATCH MAEOSTRO
*/
const matchFullCreditCard = /^(?:(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11}))$/
const isFullCreditCard = test(matchFullCreditCard)
/**
* @param {Array<RegExp>} list
* @param {*} value
* @return {Boolean} some match test
*/
function _anyMatch(list, value) {
// === regexp => test(regexp, value)
const testValue = test('_', value)
return list.some(testValue)
}
const anyMatch = curry2(_anyMatch)
/**
* @description default length validation
* @todo unused defaults
*/
const cardLength = 16
const cvcLength = 3
const matchMasterCard = /^2[0-9]|^5$|^5[1-5][0-9]{0,14}$/
const matchVisaCard = /^4[0-9]{0,15}$/
const matchAmexCard = /^3$|^3[47][0-9]{0,14}$/
const matchDinnerClubList = Object.freeze([
/^(3(0([0-5|9][0-9]{11})))$/,
/^3[689][0-9]{12}$/,
])
const matchDiscoverCardList = Object.freeze([
// unused? invalid?
// (value.match(/^6011{0,12}$/) || value.match(/^6(?:4[4-9]{2}|5[0-9]{2})[0-9]{0,14}$/))
/^6(?:011|5[0-9]{2})[0-9]{0,12}$/,
/^6(?:44|5[0-9]{2})[0-9]{0,13}$/,
/^6(?:45|5[0-9]{2})[0-9]{0,13}$/,
/^6(?:46|5[0-9]{2})[0-9]{0,13}$/,
/^6(?:47|5[0-9]{2})[0-9]{0,13}$/,
/^6(?:49|5[0-9]{2})[0-9]{0,13}$/,
/^6(?:5|5[0-9]{2})[0-9]{0,14}$/,
])
const isNumberSecurityCode = /^\d+$/
const isVisaCard = test(matchVisaCard)
const isMasterCard = test(matchMasterCard)
const isAmexCard = test(matchAmexCard)
const isDinnerClubCard = anyMatch(matchDinnerClubList)
const isDiscoverCard = anyMatch(matchDiscoverCardList)
const isInvalidCard = card => isEmpty(card.owns)
/**
* @todo isWesternUnion
*/
const masterCard = Object.freeze({
owns: '002',
cardLength: 16,
cvcLength: 3,
match: matchMasterCard,
is: isMasterCard,
icon: 'mastercard',
})
const visaCard = Object.freeze({
owns: '001',
cardLength: 16,
cvcLength: 3,
match: matchVisaCard,
is: isVisaCard,
icon: 'visa',
})
// @name americanexpress
const amexCard = Object.freeze({
owns: '003',
cardLength: 15,
cvcLength: 4,
match: matchAmexCard,
is: isAmexCard,
icon: 'amex',
})
const discoverCard = Object.freeze({
owns: '004',
cardLength: 16,
cvcLength: 3,
is: isDiscoverCard,
match: matchDiscoverCardList,
icon: 'discovery',
})
const dinnerClubCard = Object.freeze({
owns: '005',
cardLength: 15,
cvcLength: 3,
match: matchDinnerClubList,
is: isDinnerClubCard,
icon: 'dinnerclub',
})
const unknownCard = Object.freeze({
owns: '',
cardLength: 0 - 16,
cvcLength: 0 - 3,
match: matchFullCreditCard,
is: isFullCreditCard,
icon: 'unknown',
})
const invalidCard = Object.freeze({
owns: '',
cardLength: 0,
cvcLength: 0,
match: EMPTY_REGEXP,
is: isInvalidCard,
icon: 'invalidcard',
})
function _toCreditCardFrom(card, value) {
return {
...card,
value,
}
}
const toCreditCardFrom = curry2(_toCreditCardFrom)
const toMasterCard = toCreditCardFrom(masterCard)
const toVisaCard = toCreditCardFrom(visaCard)
const toAmexCard = toCreditCardFrom(amexCard)
const toDiscoverCard = toCreditCardFrom(discoverCard)
const toDinnerClub = toCreditCardFrom(dinnerClubCard)
const toInvalidCard = toCreditCardFrom(invalidCard)
const tounknownCard = toCreditCardFrom(unknownCard)
/**
* @param {String} value
* @return {CreditCard}
*/
function toCreditCard(value: CreditCardDataType): object {
// coersion
if (isMasterCard(value)) {
return toMasterCard(value)
} else if (isVisaCard(value)) {
return toVisaCard(value)
} else if (isAmexCard(value)) {
return toAmexCard(value)
} else if (isDiscoverCard(value)) {
return toDiscoverCard(value)
} else if (isDinnerClubCard(value)) {
return toDinnerClub(value)
} else if (isFullCreditCard(value)) {
return tounknownCard(value)
} else {
return toInvalidCard(value)
}
}
/**
* @note - this always returns a boolean, to coerce to credit card, use it
* if you benchmark and need perf boost, add cache or safety to `toInvalidCard`
*
* @param {String} value
* @return {Boolean}
*/
function isValidCreditCard(value: string): boolean {
if (isString(value) === false) {
return false
} else if (isInvalidCard(toCreditCard(value))) {
// @todo @help - unknown what todo - is not supported error?
// else if (isFullCreditCard(value))
return false
} else {
// making sure it's a full credit card...
// @todo may want `isValidPartialCreditCard`...
return isFullCreditCard(value)
}
}
function isValidSecurityCode(value: CreditCardDataType): boolean {
const coerced = isNumber(value) ? String(value) : value
const isStringWithLength = isString(coerced) && coerced.length >= 3
const isSecurityCodeValid =
isStringWithLength && !!value.match(isNumberSecurityCode)
return isSecurityCodeValid
}
export { masterCard }
export { visaCard }
export { amexCard }
export { isAmexCard }
export { discoverCard }
export { dinnerClubCard }
export { invalidCard }
export { toCreditCard }
export { isValidCreditCard }
export { isValidSecurityCode }
export default isValidCreditCard