Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support of different credit cards types #164

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
})
2 changes: 1 addition & 1 deletion dist/lib/masks/credit-card.mask.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 21 additions & 34 deletions lib/masks/credit-card.mask.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
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 CREDIT_CARD_SETTINGS = {
obfuscated: false,
issuer: 'visa-or-mastercard'
}
const defaultType = { type: 'default', gaps: [ 4, 8, 12 ], lengths: [ 16 ] }

const MASK_TRANSLATION = {
'*': val => null
'*': val => '*'
}

export default class CreditCardMask extends BaseMask {
Expand All @@ -31,17 +14,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 +39,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.map((g, i) => g + i)

const firstGap = gaps[0]
const lastGap = gaps[gaps.length - 1]
return Array.from(new Array(length + gaps.length)).map((_, i) => {
if (gaps.includes(i)) return ' '
if (settings.obfuscated && i > firstGap && i < lastGap) return '*'
return 9
}).join('')
}

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

return CREDIT_CARD_MASKS[issuer][maskType]
getCardType(value) {
return cardTypes(value)[0] || defaultType
}
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-masked-text",
"version": "1.12.3",
"version": "1.13.1",
"description": "Text and TextInput with mask for React Native applications",
"licenses": [
{
Expand Down Expand Up @@ -36,21 +36,21 @@
},
"homepage": "https://github.com/benhurott/react-native-masked-text#readme",
"dependencies": {
"moment": "2.19.3",
"credit-card-type": "^8.2.0",
"moment": "2.29.2",
"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
21 changes: 9 additions & 12 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 Expand Up @@ -4298,10 +4303,10 @@ mkdirp@^0.5.0, mkdirp@^0.5.1:
dependencies:
minimist "0.0.8"

moment@2.19.3:
version "2.19.3"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.3.tgz#bdb99d270d6d7fda78cc0fbace855e27fe7da69f"
integrity sha1-vbmdJw1tf9p4zA+6zoVeJ/59pp8=
moment@2.29.2:
version "2.29.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4"
integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==

morgan@^1.9.0:
version "1.9.1"
Expand Down Expand Up @@ -5000,14 +5005,6 @@ react-is@^16.3.1, react-is@^16.8.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==

[email protected]:
version "1.9.0"
resolved "https://registry.yarnpkg.com/react-native-masked-text/-/react-native-masked-text-1.9.0.tgz#562be7aa544eebb42c9633134a918bc78eda33da"
integrity sha512-LEWxk1Uf4kFcTX2DtE5S8N1Hd6wg6IyHFQBYfhRnw/hLOTfJSXd69PsxlnjxECuS80rBCmFCcixdfC1vtbhmug==
dependencies:
moment "2.19.3"
tinymask "^1.0.2"

[email protected]:
version "0.55.4"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.55.4.tgz#eecffada3750a928e2ddd07cf11d857ae9751c30"
Expand Down