Skip to content

Commit

Permalink
fix(payment): CHECKOUT-4655 Add support for additional card types whe…
Browse files Browse the repository at this point in the history
…n validating hosted payment form
  • Loading branch information
davidchin committed Feb 3, 2020
1 parent 08a151e commit 1253191
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 13 deletions.
5 changes: 0 additions & 5 deletions package-lock.json

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

2 changes: 0 additions & 2 deletions package.json
Expand Up @@ -50,15 +50,13 @@
"@bigcommerce/request-sender": "^0.3.0",
"@bigcommerce/script-loader": "^2.1.0",
"@types/card-validator": "^4.1.0",
"@types/credit-card-type": "^7.0.0",
"@types/iframe-resizer": "^3.5.6",
"@types/lodash": "^4.14.139",
"@types/reselect": "^2.2.0",
"@types/shallowequal": "^1.1.1",
"@types/yup": "^0.26.24",
"card-validator": "^6.2.0",
"core-js": "^3.1.2",
"credit-card-type": "^8.3.0",
"iframe-resizer": "^3.6.2",
"local-storage-fallback": "^4.1.1",
"lodash": "^4.17.15",
Expand Down
38 changes: 38 additions & 0 deletions src/common/types/card-validator.d.ts
@@ -0,0 +1,38 @@
import 'card-validator';

// Merge @types/card-validator with missing methods. We probably don't need this
// once the official package is updated with the latest type definitions.
declare module 'card-validator' {
type CardBrand =
| 'american-express'
| 'diners-club'
| 'discover'
| 'jcb'
| 'maestro'
| 'mastercard'
| 'unionpay'
| 'visa';

interface CreditCardTypeInfo {
patterns?: Array<number | [number, number]>;
niceType?: string;
type?: CardBrand;
prefixPattern?: RegExp;
exactPattern?: RegExp;
gaps?: number[];
lengths?: number[];
code?: {
name?: string;
size?: number;
};
}

interface CreditCardType {
types: { [type: string]: string };
(cardNumber: string): CreditCardTypeInfo[];
getTypeInfo(type: string): CreditCardTypeInfo;
updateCard(type: string, updates: Partial<CreditCardTypeInfo>): void;
}

export const creditCardType: CreditCardType;
}
3 changes: 1 addition & 2 deletions src/hosted-form/iframe-content/card-number-formatter.ts
@@ -1,5 +1,4 @@
import { number } from 'card-validator';
import creditCardType from 'credit-card-type';
import { creditCardType, number } from 'card-validator';
import { max } from 'lodash';

const NUMBER_SEPARATOR = ' ';
Expand Down
10 changes: 10 additions & 0 deletions src/hosted-form/iframe-content/hosted-input-validator.spec.ts
Expand Up @@ -38,6 +38,16 @@ describe('HostedInputValidator', () => {
.toEqual(validResults);
});

it('does not throw error if card number is valid 13-digit visa card number', async () => {
expect(await validator.validate({ ...validData, cardNumber: '4929 0000 0555 9' }))
.toEqual(validResults);
});

it('does not throw error if card number is valid 8-BIN discover card number', async () => {
expect(await validator.validate({ ...validData, cardNumber: '8171 9999 2766 0000' }))
.toEqual(validResults);
});

it('returns error if card number is missing', async () => {
expect(await validator.validate({ ...validData, cardNumber: '' }))
.toEqual({
Expand Down
27 changes: 23 additions & 4 deletions src/hosted-form/iframe-content/hosted-input-validator.ts
@@ -1,5 +1,4 @@
import { cvv, expirationDate, number } from 'card-validator';
import { getTypeInfo } from 'credit-card-type';
import { creditCardType, cvv, expirationDate, number } from 'card-validator';
import { includes } from 'lodash';
import { object, string, StringSchema, ValidationError } from 'yup';

Expand All @@ -13,7 +12,9 @@ import HostedInputValues from './hosted-input-values';
export default class HostedInputValidator {
constructor(
private _cardInstrument?: CardInstrument
) {}
) {
this._configureCardValidator();
}

async validate(values: HostedInputValues): Promise<HostedInputValidateResults> {
const requiredFields = Object.keys(values);
Expand Down Expand Up @@ -80,6 +81,24 @@ export default class HostedInputValidator {
}
}

private _configureCardValidator(): void {
const discoverInfo = creditCardType.getTypeInfo('discover');
const visaInfo = creditCardType.getTypeInfo('visa');

// Need to support 13 digit PAN because some gateways only provide test credit card numbers in this format.
creditCardType.updateCard('visa', {
lengths: [13, ...(visaInfo.lengths || [])],
});

// Add support for 8-BIN Discover Cards.
creditCardType.updateCard('discover', {
patterns: [
...(discoverInfo.patterns || []),
[810, 817],
],
});
}

private _getCardCodeSchema(): StringSchema {
return string()
.required('CVV is required')
Expand All @@ -102,7 +121,7 @@ export default class HostedInputValidator {
name: 'invalid_card_code',
test: (value = '') => {
const cardType = this._cardInstrument && this._mapFromInstrumentCardType(this._cardInstrument.brand);
const cardInfo = cardType && getTypeInfo(cardType);
const cardInfo = cardType && creditCardType.getTypeInfo(cardType);

return cvv(value, cardInfo && cardInfo.code ? cardInfo.code.size : undefined).isValid;
},
Expand Down
1 change: 1 addition & 0 deletions tsconfig-jest.json
Expand Up @@ -20,6 +20,7 @@
"target": "es5"
},
"include": [
"src/common/types/card-validator.d.ts",
"src/**/*.ts"
]
}
1 change: 1 addition & 0 deletions tsconfig.json
Expand Up @@ -25,6 +25,7 @@
"src/**/*.ts"
],
"files": [
"src/common/types/card-validator.d.ts",
"src/common/types/webpack.d.ts"
]
}

0 comments on commit 1253191

Please sign in to comment.