5

I'm writing a tool that onkeydown will run the current value being entered in an input box to check to see if it matches a regex for one of the 4 major types of credit cards.

I feel like it kind of works, but it's flaky so I wanted to figure out what was causing it to give faulty response (e.g. sometimes it'll output 2 values instead of one). Is it because I need to set a flag variable before looping? Upon match of the correct card, I'm just returning from the loop through the object, so I thought that'd be sufficient enough...

The criteria for the regexes were pulled from this site:

  • Visa: ^4[0-9]{12}(?:[0-9]{3})?$ All Visa card numbers start with a 4. New cards have 16 digits. Old cards have 13.

  • MasterCard: ^5[1-5][0-9]{14}$ All MasterCard numbers start with the numbers 51 through 55. All have 16 digits.

  • American Express: ^3[47][0-9]{13}$ American Express card numbers start with 34 or 37 and have 15 digits.

  • Discover: ^6(?:011|5[0-9]{2})[0-9]{12}$ Discover card numbers begin with 6011 or 65. All have 16 digits.

    $(function() {
    
    var $cardNumber = $('#js-cardnumber');
    
    var ccMap = {};
    
    ccMap.cards = {
        'amex': '^3[47][0-9]{13}$',
        'discover': '^6(?:011|5[0-9]{2})[0-9]{12}$',
        'mastercard': '^5[1-5][0-9]{14}$',
        'visa': '^4[0-9]{12}(?:[0-9]{3})?$'
    };
    
    
    $cardNumber.keydown(function() {
    for (var cardType in ccMap.cards) {
        if (ccMap.cards.hasOwnProperty(cardType)) {
            var regex = ccMap.cards[cardType];
            if (regex.match($(this).val())) {
                console.log(cardType);
                return;
            }
        }
    }
    });
    });​
    

Here's a fiddle

bob_cobb
  • 2,229
  • 11
  • 49
  • 109
  • You are doing something wrong elsewhere. After logging to console you are exiting the procedure/ending the loop, so there is no chance for two log lines being outputted. – SJuan76 Aug 11 '12 at 21:06
  • Check this out http://en.wikipedia.org/wiki/Luhn. And then this implementation is JS https://github.com/jzaefferer/jquery-validation/blob/master/jquery.validate.js#L1129 – elclanrs Aug 11 '12 at 21:11
  • I would expect `/^3[47][0-9]{13)$/` – mplungjan Aug 11 '12 at 21:13
  • In fairness, I think they're doing card-type matching rather than luhn checking, so it isn't really reinventing the wheel. That said, since you'll want a luhn check too, and it will need to spin through ensuring each character is a digit, I'd take some luhn code and then add a length and prefix check onto it, rather than this approach. – Jon Hanna Aug 11 '12 at 21:15
  • @mplungjan thanks, I've changed my regexes to follow that format. yeah this is basically just to show the image of the credit card. The actual validation is taking place server-side, so I don't think a Luhn would even be necessary. Thanks for the github link. – bob_cobb Aug 11 '12 at 21:20
  • The title of this question is scary. – Jill-Jênn Vie Aug 11 '12 at 21:25

1 Answers1

4

It seems like you're using the regular expressions in a wrong way.

If you want to check a string against a regular expression, you can use the match() method of the string:

string.match(regexp) // returns boolean

You're doing it the wrong way:

if ( regex.match($(this).val()) ) {

tries to interpret the current value as a regular expression. Must be this way:

if ( $(this).val().match(regex) ) {

You can also cache the regular expressions to make your script more efficient:

ccMap.cards = {
    'amex': /^3[47][0-9]{13}$/,  // store an actual regexp object, not a string
    // ...

// The way you test changes, now you're able to use the "test"
// method of the regexp object:
if ( regex.test($(this).val()) ) {
Niko
  • 26,516
  • 9
  • 93
  • 110
  • Oh dang. Thanks... Yeah that makes much more sense to store the regex itself rather than a string. Thanks for the quick response :) – bob_cobb Aug 11 '12 at 21:24
  • I find `match` confusing because every other regexp method works on the regex and `match` works on a string. I always use `exec` instead of `match` for this reason --> `regex.exec($(this).val())` – elclanrs Aug 11 '12 at 21:32