Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
ui-component-library / src / forms / deps / isValidCreditCard.ts
Size: Mime:
/* 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