Skip to content

Commit

Permalink
Support of different credit cards types
Browse files Browse the repository at this point in the history
- added support of credit card with different length and formatting
- refactored test for credit card mask
  • Loading branch information
AleksandrZhukov committed May 31, 2019
1 parent 0b04ce4 commit d9c815f
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 81 deletions.
89 changes: 46 additions & 43 deletions __tests__/mask/credit-card.mask.test.js
Original file line number Diff line number Diff line change
@@ -1,106 +1,109 @@
import { CreditCardMask } from '../../lib/masks'

const testCards = {
visa: '4263982640269299',
mastercard: '5425233430109903',
amex: '374245455400126',
maestro: '6759649826438453'
}

test('getType results credit-card', () => {
var expected = 'credit-card'
var received = CreditCardMask.getType()

expect(received).toBe(expected)
})

test('1234123412341234 results 1234 1234 1234 1234', () => {
test('basic formatting', () => {
var mask = new CreditCardMask()
var expected = '1234 1234 1234 1234'
var received = mask.getValue('1234123412341234')
var expected = '4263 9826 4026 9299'
var received = mask.getValue(testCards.visa)

expect(received).toBe(expected)
})

test('1234123412341234 obfuscated true results 1234 **** **** 1234', () => {
test('obfuscated formatting', () => {
var mask = new CreditCardMask()
var expected = '1234 **** **** 1234'
var received = mask.getValue('1234123412341234', {
var expected = '4263 **** **** 9299'
var received = mask.getValue(testCards.visa, {
obfuscated: true
})

expect(received).toBe(expected)
})

test('1234123412341234 obfuscated false results 1234 1234 1234 1234', () => {
test('correct raw value of basic formatting', () => {
var mask = new CreditCardMask()
var expected = '1234 1234 1234 1234'
var received = mask.getValue('1234123412341234', {
obfuscated: false
})
var received = mask.getValue(testCards.visa)

expect(received).toBe(expected)
})

test('1234123412341234 obfuscated false results 1234 1234 1234 1234 and raw value [1234, 1234, 1234, 1234]', () => {
var mask = new CreditCardMask()
var options = {
obfuscated: false
}

var expected = '1234 1234 1234 1234'
var received = mask.getValue('1234123412341234', options)

var expectedRawValue = ['1234', '1234', '1234', '1234']
var receivedRawValue = mask.getRawValue(received, options)

expect(received).toBe(expected)
var expectedRawValue = ['4263', '9826', '4026', '9299']
var receivedRawValue = mask.getRawValue(received)

expectedRawValue.forEach((val, index) => {
expect(val).toBe(receivedRawValue[index])
})
})

test('1234123412341234 obfuscated true results 1234 **** **** 1234 and raw value [1234, ****, ****, 1234]', () => {
test('correct raw value of obfuscated formatting', () => {
var mask = new CreditCardMask()
var options = {
obfuscated: true
}

var expected = '1234 **** **** 1234'
var received = mask.getValue('1234123412341234', options)
var received = mask.getValue(testCards.visa, options)

var expectedRawValue = ['1234', '****', '****', '1234']
var expectedRawValue = ['4263', '****', '****', '9299']
var receivedRawValue = mask.getRawValue(received, options)

expect(received).toBe(expected)

expectedRawValue.forEach((val, index) => {
expect(val).toBe(receivedRawValue[index])
})
})

test('getMask returns 9999 9999 9999 9999', () => {
test('returns correct default regular mask', () => {
var mask = new CreditCardMask()
var expected = '9999 9999 9999 9999'
var received = mask.getMask()

expect(received).toBe(expected)
})

test('getMask obfuscated returns 9999 **** **** 9999', () => {
test('returns correct default obfuscated mask', () => {
var mask = new CreditCardMask()
var expected = '9999 **** **** 9999'
var received = mask.getMask('', { obfuscated: true })
var received = mask.getMask(null, { obfuscated: true })

expect(received).toBe(expected)
})

test('basic formatting of amex card', () => {
var mask = new CreditCardMask()
var expected = '3742 454554 00126'
var received = mask.getValue(testCards.amex)

expect(received).toBe(expected)
})

test('get masked value with amex issuer must return 1234 123456 12345', () => {
test('obfuscated formatting of amex card', () => {
var mask = new CreditCardMask()
var expected = '1234 123456 12345'
var received = mask.getValue('123412345612345', { issuer: 'amex' })
var expected = '3742 ****** 00126'
var received = mask.getValue(testCards.amex, { obfuscated: true })

expect(received).toBe(expected)
})

test('getMask with diners issuer must return 1234 123456 1234', () => {
test('basic formatting of mastercard card', () => {
var mask = new CreditCardMask()
var expected = '1234 123456 1234'
var received = mask.getValue('12341234561234', { issuer: 'diners' })
var expected = '5425 2334 3010 9903'
var received = mask.getValue(testCards.mastercard)

expect(received).toBe(expected)
})
})

test('basic formatting of maestro card', () => {
var mask = new CreditCardMask()
var expected = '6759 6498 2643 8453'
var received = mask.getValue(testCards.maestro)

expect(received).toBe(expected)
})
58 changes: 25 additions & 33 deletions lib/masks/credit-card.mask.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
import cardTypes from 'credit-card-type'
import BaseMask from './_base.mask'
import CustomMask from './custom.mask'

const CREDIT_CARD_MASKS = {
'visa-or-mastercard': {
regular: '9999 9999 9999 9999',
obfuscated: '9999 **** **** 9999'
},
'amex': {
regular: '9999 999999 99999',
obfuscated: '9999 ****** 99999'
},
'diners': {
regular: '9999 999999 9999',
obfuscated: '9999 ****** 9999'
},
}
const defaultType = { type: 'default', gaps: [ 4, 8, 12 ], lengths: [ 16 ] }

const CREDIT_CARD_SETTINGS = {
obfuscated: false,
issuer: 'visa-or-mastercard'
const MASK_TRANSLATION = {
'*': val => '*'
}

const MASK_TRANSLATION = {
'*': val => null
function setCharAt(str,index,chr) {
if(index > str.length-1) return str;
return str.substr(0,index) + chr + str.substr(index+1);
}

export default class CreditCardMask extends BaseMask {
Expand All @@ -31,17 +19,16 @@ export default class CreditCardMask extends BaseMask {
}

getValue(value, settings) {
let selectedMask = this.getMask(value, settings)
return CustomMask.shared.getValue(value, {
mask: selectedMask,
mask: this.getMask(value, settings),
translation: MASK_TRANSLATION
})
}

validate(value, settings) {
validate(value) {
if (!!value) {
let selectedMask = this.getMask(value, settings)
return value.length === selectedMask.length
const type = this.getCardType(value)
return type.lengths.includes(value.length)
}

return true
Expand All @@ -57,16 +44,21 @@ export default class CreditCardMask extends BaseMask {
})
}

getMask(value, settings) {
let mergedSettings = super.mergeSettings(CREDIT_CARD_SETTINGS, settings)
const selectedMask = this._selectMask(mergedSettings.issuer, mergedSettings.obfuscated)

return selectedMask
getMask(value, settings = {}) {
const type = this.getCardType(value)
const length = Math.max(...type.lengths)
const gaps = type.gaps
const firstGap = gaps[0];
const lastGap = gaps[gaps.length - 1];
let mask = '9'.repeat(length + gaps.length)
if (settings.obfuscated) {
mask = mask.substr(0, firstGap) + '*'.repeat(lastGap - firstGap + 2) + mask.substr(lastGap + 2)
}
gaps.forEach((index, i) => mask = setCharAt(mask, index + i, ' '))
return mask
}

_selectMask(issuer, obfuscated) {
const maskType = obfuscated ? 'obfuscated' : 'regular'

return CREDIT_CARD_MASKS[issuer][maskType]
getCardType(value) {
return cardTypes(value)[0] || defaultType
}
}
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,21 @@
},
"homepage": "https://github.com/benhurott/react-native-masked-text#readme",
"dependencies": {
"credit-card-type": "^8.2.0",
"moment": "2.19.3",
"tinymask": "^1.0.2"
},
"devDependencies": {
"@types/react-native": "*",
"@types/react": "*",
"@types/react-native": "*",
"babel-cli": "^6.26.0",
"rimraf": "^2.6.2",
"react": "16.3.1",
"react-native": "0.55.4",
"babel-jest": "23.4.2",
"babel-preset-react-native": "4.0.0",
"jest": "23.5.0",
"react": "16.3.1",
"react-native": "0.55.4",
"react-test-renderer": "16.3.1",
"react-native-masked-text": "1.9.0"
"rimraf": "^2.6.2"
},
"jest": {
"preset": "react-native"
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1998,6 +1998,11 @@ create-react-class@^15.6.3:
loose-envify "^1.3.1"
object-assign "^4.1.1"

credit-card-type@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/credit-card-type/-/credit-card-type-8.2.0.tgz#507a43b257f0f3b8bd4d4b339a419741fc7e6236"
integrity sha512-+1kZKW717eDTbZ1/EjkEi4+UrD2SygU2Iujn/VlrMHXloGW/y5GqPlQ/KgT4gaoDmWyufBfm/CAzNkpeccfMtA==

cross-spawn@^5.0.1, cross-spawn@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
Expand Down

0 comments on commit d9c815f

Please sign in to comment.