frontend Aug 07

How to check Credit Card type with Javascript

3 min read –

Antonio: You know what’s the dumbest UX?

When you have to select the credit card type on payment form. Can’t they just figure it out from the credit card numbers?

Me: Err… it’s not that simple 😅.

So a while ago I had to implement a credit card type check on the GFNY.cc because you can’t use Amex with Colombian Pesos on the Bluesnap platform (who knew 🤷🏻‍♂️:).

I’ve seen this functionality gazillion times in the vastness of the Web so I figured it’d be easy to implement – 20 blog posts later I wasn’t so sure anymore.

  • Complicated Regex:
^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}$

If you have no problem following this pattern for the Switch Card then you’re way out of my League master 🙇‍♂️.

But if you are like me, understand the basics of Regex but get a bit lost once the conditions and Capturing groups come in, do proceed. I do regularly C/P the code from the internet, but I C/P the code that I understand. If above-shown Regex was buggy/had to be adjusted/changed/improved (which isn’t unexpected, see point 1.) I’d be ****ed 😅.


Including another library to solve every problem is a good example of “Throw the money at it ” attitude
  • Credit Card validation libraries:there are some awesome Credit card validation libraries but this solution comes with a set of problems: They usually include (a lot) more than I need – there can be (in this specific case are) thousands of tasks on a project, if I solved most of them by just including another library (“Throw money at it” attitude) in the project it would quickly snowball to a point of total un-maintainability.

Comparing sizes of some popular CC type/validation libraries to React

Credit Card Types

Amex, Visa & China Union Pay ❤️ are the only ones that don’t cause the trouble:

Visa: if it starts with a 4 it’s a Visa [16 digits (13 in some special cases)]

American Express: starts with 34 or 37 [15 digits]

CUP: starts with 62 or 81 [16–19 digits, increasing the complexity but still everything under control]

Now the fun begins:

Mastercard: First 2 digits can range from 51 to 55, but as mentioned above since 2017. they also cover the 2221-2720 range [luckily in both cases 16 digits]

Discover(🙈 ): if it starts with 64 or 65 [16–19 digits] it’s definitely Discover, but…

It can also start with any of these 6011, 622126 – 622925, 624000 – 626999, 628200 – 628899 (😭 )

Diners: 36 definitely Diners [14-19 digits], but also 300–305, 3095, 38–39 [note that these range from 16-19 digits]

JBC: covers the range 3528-3589 [16–19 digits]

Vanilla Javascript solution

This is by no means an ideal solution. The idea was to reduce Regex complexity and move all the conditional logic and options to Javascript (where it belongs (?)). In this short snippet I’ve only covered the cases I needed for the feature so feel free to add on it/adjust it to your needs.

export function creditCardType(cc: string) {
  let amex = new RegExp('^3[47][0-9]{13}$');
  let visa = new RegExp('^4[0-9]{12}(?:[0-9]{3})?$');
  let cup1 = new RegExp('^62[0-9]{14}[0-9]*$');
  let cup2 = new RegExp('^81[0-9]{14}[0-9]*$');

  let mastercard = new RegExp('^5[1-5][0-9]{14}$');
  let mastercard2 = new RegExp('^2[2-7][0-9]{14}$');

  let disco1 = new RegExp('^6011[0-9]{12}[0-9]*$');
  let disco2 = new RegExp('^62[24568][0-9]{13}[0-9]*$');
  let disco3 = new RegExp('^6[45][0-9]{14}[0-9]*$');
  
  let diners = new RegExp('^3[0689][0-9]{12}[0-9]*$');
  let jcb =  new RegExp('^35[0-9]{14}[0-9]*$');


  if (visa.test(cc)) {
    return 'VISA';
  }
  if (amex.test(cc)) {
    return 'AMEX';
  }
  if (mastercard.test(cc) || mastercard2.test(cc)) {
    return 'MASTERCARD';
  }
  if (disco1.test(cc) || disco2.test(cc) || disco3.test(cc)) {
    return 'DISCOVER';
  }
  if (diners.test(cc)) {
    return 'DINERS';
  }
  if (jcb.test(cc)) {
    return 'JCB';
  }
  if (cup1.test(cc) || cup2.test(cc)) {
    return 'CHINA_UNION_PAY';
  }
  return undefined;
}

Wrap-up


Definitely not extorting interaction from the readers

That’s all folks! Definitely let me know if you find some mistakes or some *hint* uncovered cases *hint* – definitely not extorting interaction from my readers 😇 . But seriously, if you want to find out those small improvements & nuance let me know in the comment section 👇🏻👇🏻👇🏻👇🏻👇🏻.