Skip to content

Commit 5125f68

Browse files
authored
Merge pull request #532 from maxmind/nlogan/8-digit-iin-2-digit-lastdigits
Add support for 8 digit IINs and 2 digit last_digits
2 parents 765129a + 1df8ee5 commit 5125f68

File tree

6 files changed

+167
-22
lines changed

6 files changed

+167
-22
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ CHANGELOG
2222
`response.ipAddress.traits.mobileCountryCode` and
2323
`response.ipAddress.traits.mobileNetworkCode`. We expect this data to be
2424
available by late January, 2022.
25+
* `creditCard.last4digits` has been deprecated in favor of `creditCard.lastDigits`
26+
and will be removed in a future release. `lastDigits`/`last4digits` also now
27+
supports two digit values in addition to the previous four digit values.
28+
* Eight digit `creditCard.issuerIdNumber` inputs are now supported in addition to
29+
the previously accepted six digit `issuerIdNumber`. In most cases, you should
30+
send the last four digits for `creditCard.lastDigits`. If you send a
31+
`issuerIdNumber` that contains an eight digit IIN, and if the credit card brand
32+
is not one of the following, you should send the last two digits for
33+
`lastDigits`:
34+
* `Discover`
35+
* `JCB`
36+
* `Mastercard`
37+
* `UnionPay`
38+
* `Visa`
2539

2640
4.3.0 (2021-08-31)
2741
------------------

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ try {
212212
bankPhoneNumber: '123-123-1234',
213213
cvvResult: 'B',
214214
issuerIdNumber: '411111',
215-
last4digits: '1234',
215+
lastDigits: '1234',
216216
token: 'a_token',
217217
was3DSecureSuccessful: true,
218218
}),

src/request/credit-card.spec.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ describe('CreditCard()', () => {
1010
${'issuerIdNumber is too short'} | ${'issuerIdNumber'} | ${'12345'}
1111
${'issuerIdNumber has letters'} | ${'issuerIdNumber'} | ${'12345a'}
1212
${'issuerIdNumber has non-alphanumeric characters'} | ${'issuerIdNumber'} | ${'12345!'}
13-
${'last4digits is too long'} | ${'last4digits'} | ${'12345'}
14-
${'last4digits is too short'} | ${'last4digits'} | ${'123'}
15-
${'last4digits has letters'} | ${'last4digits'} | ${'12a'}
16-
${'last4digits has non-alphanumeric characters'} | ${'last4digits'} | ${'154!'}
13+
${'lastDigits is too long'} | ${'lastDigits'} | ${'12345'}
14+
${'lastDigits is too short'} | ${'lastDigits'} | ${'123'}
15+
${'lastDigits has letters'} | ${'lastDigits'} | ${'12a'}
16+
${'lastDigits has non-alphanumeric characters'} | ${'lastDigits'} | ${'154!'}
1717
${'token is a PAN'} | ${'token'} | ${'4485921507912924'}
1818
${'token is numbers'} | ${'token'} | ${'432312'}
1919
${'token is some string phrase'} | ${'token'} | ${'this is invalid'}
@@ -55,4 +55,24 @@ describe('CreditCard()', () => {
5555
})
5656
).not.toThrow();
5757
});
58+
59+
it('last4digits getter aliases lastDigits', () => {
60+
const cc = new CreditCard({
61+
issuerIdNumber: '411111',
62+
lastDigits: '1234',
63+
});
64+
65+
expect(cc.lastDigits).toBe('1234');
66+
expect(cc.last4digits).toBe(cc.lastDigits);
67+
});
68+
69+
it('last4digits setter aliases lastDigits', () => {
70+
const cc = new CreditCard({
71+
issuerIdNumber: '411111',
72+
});
73+
cc.last4digits = '1234';
74+
75+
expect(cc.lastDigits).toBe('1234');
76+
expect(cc.last4digits).toBe(cc.lastDigits);
77+
});
5878
});

src/request/credit-card.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ export interface CreditCardProps {
77
*/
88
issuerIdNumber?: string;
99
/**
10-
* The last four digits of the credit card number.
10+
* The last digits of the credit card number.
11+
*
12+
* @deprecated Use lastDigits instead
1113
*/
1214
last4digits?: string;
15+
/**
16+
* The last digits of the credit card number.
17+
*/
18+
lastDigits?: string;
1319
/**
1420
* A token uniquely identifying the card. This should not be the actual
1521
* credit card number.
@@ -50,8 +56,8 @@ export interface CreditCardProps {
5056
}
5157

5258
const singleChar = /^[A-Za-z0-9]$/;
53-
const issuerIdNumberRegex = /^[0-9]{6}$/;
54-
const last4Regex = /^[0-9]{4}$/;
59+
const issuerIdNumberRegex = /^(?:[0-9]{6}|[0-9]{8})$/;
60+
const lastDigitsRegex = /^(?:[0-9]{2}|[0-9]{4})$/;
5561
const tokenRegex = /^(?![0-9]{1,19}$)[\u0021-\u007E]{1,255}$/;
5662

5763
/**
@@ -60,8 +66,8 @@ const tokenRegex = /^(?![0-9]{1,19}$)[\u0021-\u007E]{1,255}$/;
6066
export default class CreditCard implements CreditCardProps {
6167
/** @inheritDoc CreditCardProps.issuerIdNumber */
6268
public issuerIdNumber?: string;
63-
/** @inheritDoc CreditCardProps.last4digits */
64-
public last4digits?: string;
69+
/** @inheritDoc CreditCardProps.lastDigits */
70+
public lastDigits?: string;
6571
/** @inheritDoc CreditCardProps.token */
6672
public token?: string;
6773
/** @inheritDoc CreditCardProps.bankName */
@@ -105,12 +111,16 @@ export default class CreditCard implements CreditCardProps {
105111
);
106112
}
107113

114+
if (creditCard.last4digits != null) {
115+
creditCard.lastDigits = creditCard.last4digits;
116+
}
117+
108118
if (
109-
creditCard.last4digits != null &&
110-
!last4Regex.test(creditCard.last4digits)
119+
creditCard.lastDigits != null &&
120+
!lastDigitsRegex.test(creditCard.lastDigits)
111121
) {
112122
throw new ArgumentError(
113-
`The last 4 credit card digits (last4digits) ${creditCard.last4digits} are of the wrong format.`
123+
`The last credit card digits (lastDigits) ${creditCard.lastDigits} are of the wrong format.`
114124
);
115125
}
116126

@@ -122,4 +132,20 @@ export default class CreditCard implements CreditCardProps {
122132

123133
Object.assign(this, creditCard);
124134
}
135+
136+
/** Get the last digits of the credit card number.
137+
*
138+
* @deprecated Use lastDigits instead
139+
*/
140+
public get last4digits() {
141+
return this.lastDigits;
142+
}
143+
144+
/** Set the last digits of the credit card number.
145+
*
146+
* @deprecated Use lastDigits instead
147+
*/
148+
public set last4digits(lastDigits: string | undefined) {
149+
this.lastDigits = lastDigits;
150+
}
125151
}

src/request/transaction.spec.ts

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,7 @@ describe('Transaction()', () => {
266266

267267
expect(test.toString()).toContain(deviceString);
268268

269-
expect(test.toString()).toContain(
270-
'"credit_card":{"last_4_digits":"1234"}'
271-
);
269+
expect(test.toString()).toContain('"credit_card":{"last_digits":"1234"}');
272270
});
273271

274272
it('it handles optional order field', () => {
@@ -317,7 +315,7 @@ describe('Transaction()', () => {
317315
});
318316

319317
describe('key casing conversion', () => {
320-
describe('`creditCard.last4digits` => `creditCard.last_4_digits`', () => {
318+
describe('`creditCard.lastDigits/last4digits` => `creditCard.lastDigits`', () => {
321319
test('typed value is mapped', () => {
322320
const test = JSON.parse(
323321
new Transaction({
@@ -328,7 +326,7 @@ describe('Transaction()', () => {
328326
}).toString()
329327
);
330328

331-
expect(test.credit_card).toHaveProperty('last_4_digits', '1234');
329+
expect(test.credit_card).toHaveProperty('last_digits', '1234');
332330
});
333331

334332
test('null value is mapped', () => {
@@ -343,7 +341,35 @@ describe('Transaction()', () => {
343341
}).toString()
344342
);
345343

346-
expect(test.credit_card).toHaveProperty('last_4_digits', null);
344+
expect(test.credit_card).toHaveProperty('last_digits', null);
345+
});
346+
347+
test('typed value is mapped', () => {
348+
const test = JSON.parse(
349+
new Transaction({
350+
device: new Device({
351+
ipAddress: '1.1.1.1',
352+
}),
353+
creditCard: new CreditCard({ lastDigits: '1234' }),
354+
}).toString()
355+
);
356+
357+
expect(test.credit_card).toHaveProperty('last_digits', '1234');
358+
});
359+
360+
test('null value is mapped', () => {
361+
const test = JSON.parse(
362+
new Transaction({
363+
device: new Device({
364+
ipAddress: '1.1.1.1',
365+
}),
366+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
367+
// @ts-ignore explicit null
368+
creditCard: new CreditCard({ lastDigits: null }),
369+
}).toString()
370+
);
371+
372+
expect(test.credit_card).toHaveProperty('last_digits', null);
347373
});
348374
});
349375

@@ -415,4 +441,63 @@ describe('Transaction()', () => {
415441
});
416442
});
417443
});
444+
445+
describe('6 or 8 digit iins and 2 or 4 digit lastDigits', () => {
446+
it('it handles 8 digit iins with 2 digit lastDigits', () => {
447+
const test = new Transaction({
448+
creditCard: new CreditCard({
449+
issuerIdNumber: '12345678',
450+
lastDigits: '12',
451+
}),
452+
device: new Device({
453+
ipAddress: '1.1.1.1',
454+
sessionAge: 100,
455+
}),
456+
});
457+
458+
expect(isJSON(test.toString())).toBe(true);
459+
460+
expect(test.toString()).toContain(
461+
'"credit_card":{"issuer_id_number":"12345678","last_digits":"12"}'
462+
);
463+
});
464+
465+
it('it handles 8 digit iins with 4 digit lastDigits', () => {
466+
const test = new Transaction({
467+
creditCard: new CreditCard({
468+
issuerIdNumber: '12345678',
469+
lastDigits: '1234',
470+
}),
471+
device: new Device({
472+
ipAddress: '1.1.1.1',
473+
sessionAge: 100,
474+
}),
475+
});
476+
477+
expect(isJSON(test.toString())).toBe(true);
478+
479+
expect(test.toString()).toContain(
480+
'"credit_card":{"issuer_id_number":"12345678","last_digits":"1234"}'
481+
);
482+
});
483+
484+
it('it handles 6 digit iins with 2 digit lastDigits', () => {
485+
const test = new Transaction({
486+
creditCard: new CreditCard({
487+
issuerIdNumber: '123456',
488+
lastDigits: '12',
489+
}),
490+
device: new Device({
491+
ipAddress: '1.1.1.1',
492+
sessionAge: 100,
493+
}),
494+
});
495+
496+
expect(isJSON(test.toString())).toBe(true);
497+
498+
expect(test.toString()).toContain(
499+
'"credit_card":{"issuer_id_number":"123456","last_digits":"12"}'
500+
);
501+
});
502+
});
418503
});

src/request/transaction.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,10 @@ export default class Transaction {
119119

120120
if (
121121
sanitized.creditCard &&
122-
Object.prototype.hasOwnProperty.call(sanitized.creditCard, 'last4digits')
122+
Object.prototype.hasOwnProperty.call(sanitized.creditCard, 'lastDigits')
123123
) {
124-
sanitized.creditCard.last_4_digits = sanitized.creditCard.last4digits;
125-
delete sanitized.creditCard.last4digits;
124+
sanitized.creditCard.last_digits = sanitized.creditCard.lastDigits;
125+
delete sanitized.creditCard.lastDigits;
126126
}
127127

128128
if (

0 commit comments

Comments
 (0)