Skip to content

Commit 33db360

Browse files
committed
feat: added support to get the card brand
1 parent aac9daa commit 33db360

File tree

3 files changed

+78
-5
lines changed

3 files changed

+78
-5
lines changed

README.md

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# React Card Brand
22

3-
> A zero-dependency React Hook to show the brand from a card type.
3+
> A zero-dependency React Hook to show and get the brand from a card type.
44
55
## Installation
66

@@ -33,6 +33,26 @@ export default function Example() {
3333
}
3434
```
3535

36+
### Get the brand from a card number
37+
38+
You can use the `getCardType` function to get the brand from a card number.
39+
40+
```js
41+
import React from 'react';
42+
import { useCardBrand, images } from 'react-card-brand';
43+
44+
export default function Example() {
45+
const { getSvgProps, getCardBrand } = useCardBrand();
46+
const type = getCardBrand('4242424242424242');
47+
48+
return (
49+
<div>
50+
<svg {...getSvgProps({ type, images })} />
51+
</div>
52+
);
53+
}
54+
```
55+
3656
## Community
3757

3858
All feedback and suggestions are welcome!

src/useCardBrand.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,34 @@
11
import * as React from 'react';
2+
import { getCardBrand } from './utils/validation';
23

34
interface UseCardBrandProps {
45
images?: {
56
[key: string]: React.ReactNode | JSX.Element;
67
};
8+
cardNumber?: string;
79
ariaLabel?: string;
8-
type?: string;
910
}
1011

1112
export default function useCardBrand() {
1213
const getSvgProps = React.useCallback((props: UseCardBrandProps = {}) => {
13-
const images = props.images || {};
14+
const images = props.images ?? {};
15+
const type = 'placeholder';
1416

1517
return React.useMemo(
1618
() => ({
1719
'aria-label': props.ariaLabel ?? 'Placeholder card',
18-
children: images[props.type ?? 'placeholder'] || images,
20+
children: images[type] || images,
1921
width: '1.5em',
2022
height: '1em',
2123
viewBox: '0 0 24 16',
2224
...props,
2325
}),
24-
[props.ariaLabel, props.type],
26+
[props],
2527
);
2628
}, []);
2729

2830
return {
2931
getSvgProps,
32+
getCardBrand,
3033
};
3134
}

src/utils/validation.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
enum CardType {
2+
Visa = 'visa',
3+
Mastercard = 'mastercard',
4+
Amex = 'amex',
5+
Diners = 'diners',
6+
Discover = 'discover',
7+
}
8+
9+
type CardTypeConfig = { size: number[]; prefixes: string[] };
10+
type CardTypes = { [key in CardType]: CardTypeConfig };
11+
12+
const options: CardTypes = {
13+
visa: { size: [13, 16], prefixes: ['4'] },
14+
mastercard: { size: [16], prefixes: ['51', '52', '53', '54', '55'] },
15+
amex: { size: [15], prefixes: ['34', '37'] },
16+
diners: {
17+
size: [14],
18+
prefixes: ['300', '301', '302', '303', '304', '305', '36', '38'],
19+
},
20+
discover: {
21+
size: [16],
22+
prefixes: ['6011', '644', '645', '646', '647', '648', '649', '65'],
23+
},
24+
};
25+
26+
/**
27+
* Returns the card type based on the card number
28+
* Accepts different formats of card number:
29+
* - 4242 4242 4242 4242
30+
* - 4242-4242-4242-4242
31+
* - 4242424242424242
32+
* @param cardNumber {string} - The card number to be validated
33+
* @returns The card type name
34+
*/
35+
export const getCardBrand = (cardNumber: string) => {
36+
const formattedNumber = cardNumber.replace(/[ -]/g, ''); // Removing spaces from the card number
37+
const validateCode = formattedNumber.substring(0, 4); // Getting the first four gits of the card number
38+
39+
for (const [option, config] of Object.entries(options)) {
40+
const { size, prefixes } = config;
41+
if (size.includes(formattedNumber.length)) {
42+
const testCard = new RegExp(`^(${prefixes.join('|')})`);
43+
if (testCard.test(validateCode)) {
44+
return option;
45+
}
46+
}
47+
}
48+
49+
return 'placeholder';
50+
};

0 commit comments

Comments
 (0)