JavaScript/jQuery Credit Card Validation

on in Blog
Last modified on

This is a collection of JavaScript and jQuery snippets to validate credit card details without requiring any library.

JavaScript

/*
 * Display error message based on current element's data attributes
 */
function cgToggleError(element, status) {
    var errorMessage = $(element).data('validation-error-msg'),
        errorContainer = $(element).data('validation-error-msg-container');

    $(element).removeClass().addClass(status);

    if (status === 'valid') {
        $(errorContainer).html(errorMessage).hide();
    } else if (status === 'invalid') {
        $(errorContainer).html(errorMessage).show();
    }
}

/*
 * Format a date as MM/YY
 */
function cgFormatExpiryDate(e) {
    var inputChar = String.fromCharCode(event.keyCode);
    var code = event.keyCode;
    var allowedKeys = [8];
    if (allowedKeys.indexOf(code) !== -1) {
        return;
    }

    event.target.value = event.target.value.replace(
        /^([1-9]\/|[2-9])$/g, '0$1/' // 3 > 03/
    ).replace(
        /^(0[1-9]|1[0-2])$/g, '$1/' // 11 > 11/
    ).replace(
        /^([0-1])([3-9])$/g, '0$1/$2' // 13 > 01/3
    ).replace(
        /^(0?[1-9]|1[0-2])([0-9]{2})$/g, '$1/$2' // 141 > 01/41
    ).replace(
        /^([0]+)\/|[0]+$/g, '0' // 0/ > 0 and 00 > 0
    ).replace(
        /[^\d\/]|^[\/]*$/g, '' // To allow only digits and `/`
    ).replace(
        /\/\//g, '/' // Prevent entering more than 1 `/`
    );
}

/*
 * Check if date element is valid and add a visual hint
 */
function cgDateValidate(whatDate) {
    var currVal = whatDate;

    if (currVal === '') {
        return false;
    }

    var rxDatePattern = /^(\d{1,2})(\/|-)(\d{1,2})(\/|-)(\d{4})$/;
    var dtArray = currVal.match(rxDatePattern);

    if (dtArray == null) {
        return false;
    }

    // Check for dd/mm/yyyy format
    var dtDay = dtArray[1],
        dtMonth= dtArray[3],
        dtYear = dtArray[5];

    if (dtMonth < 1 || dtMonth > 12) {
        return false;
    } else if (dtDay < 1 || dtDay> 31) {
        return false;
    } else if ((dtMonth==4 || dtMonth==6 || dtMonth==9 || dtMonth==11) && dtDay ==31) {
        return false;
    } else if (dtMonth == 2) {
        var isleap = (dtYear % 4 == 0 && (dtYear % 100 != 0 || dtYear % 400 == 0));
        if (dtDay> 29 || (dtDay ==29 && !isleap)) {
            return false;
        }
    }

    return true;
}

/*
 * Credit card expiry date formatting (real-time)
 */
$(document).on('keyup blur', '.cardExpiry', function(event) {
    var currentDate = new Date();
    var currentMonth = ("0" + (currentDate.getMonth() + 1)).slice(-2);
    var currentYear = String(currentDate.getFullYear()).slice(-2);

    var cardExpiryArray = $('.cardExpiry').val().split('/');
    var userMonth = cardExpiryArray[0],
        userYear = cardExpiryArray[1];

    if ($('.cardExpiry').val().length !== 5) {
        cgToggleError($(this), 'invalid');
    } else if (userYear < currentYear) {
        cgToggleError($(this), 'invalid');
    } else if (userYear <= currentYear && userMonth < currentMonth) { cgToggleError($(this), 'invalid'); } else if (userMonth > 12) {
        cgToggleError($(this), 'invalid');
    } else {
        cgToggleError($(this), 'valid');
    }

    cgFormatExpiryDate(event);
});

/*
 * Credit card CVV disallow letters (real-time)
 */
$(document).on('keyup', '.cardCVV', function(event) {
    event.target.value = event.target.value.replace(/[^\d\/]|^[\/]*$/g, '');
});

/*
 * Credit card CVV length check
 */
$(document).on('blur', '.cardCVV', function(e) {
    if ($('#cardCVV').val().length < 3) {
        cgToggleError($(this), 'invalid');
    }
});

/*
 * Credit card validation
 */
function cgCheckLuhn(input) {
    var sum = 0,
        numdigits = input.length;
    var parity = numdigits % 2;

    for (var i=0; i < numdigits; i++) { var digit = parseInt(input.charAt(i)); if (i % 2 == parity) { digit *= 2; } if (digit > 9) {
            digit -= 9;
        }
        sum += digit;
    }

    return (sum % 10) == 0;
}

function cgDetectCard(input) {
    var typeTest = 'u',
        ltest1 = 16,
        ltest2 = 16;
        ltest3 = 'none';

    if (/^4/.test(input)) {
        typeTest = 'v';
        ltest1 = 13;
        ltest3 = 'VISA';
    } else if (/^5[1-5]/.test(input)) {
        typeTest = 'm';
        ltest3 = 'MASTERCARD';
    } else if (/^6(011|4[4-9]|5)/.test(input)) {
        typeTest = 'd';
        ltest3 = 'VISADEBIT';
    }

    return [typeTest,ltest1,ltest2,ltest3];
}

/*
 * Credit card Luhn validation (real-time)
 */
$(document).on('keyup', '.cardNumber', function() {
    var val = this.value,
        val = val.replace(/[^0-9]/g, ''),
        detected = cgDetectCard(val),
        errorClass = 'invalid',
        luhnCheck = cgCheckLuhn(val),
        valueCheck = (val.length == detected[1] || val.length == detected[2]);

    if ($('body').hasClass('inline-ab')) {
        cgToggleError($(this), 'invalid');
    }

    if (luhnCheck && valueCheck) {
        errorClass = 'valid';
        $('#cardType').val(detected[3]);
    } else if (valueCheck || val.length > detected[2]) {
        errorClass = 'invalid';
    }

    if ($('body').hasClass('inline-ab')) {
        cgToggleError($(this), errorClass);
        cgToggleError($(this), 'cc ' + detected[0] + ' ' + errorClass);
    }
    $(this).addClass('cc ' + detected[0] + ' ' + errorClass);
});

/*
 * Credit card digit formatting (real-time)
 */
$(document).on('keypress change blur', '.cardNumber', function() {
    $(this).val(function(index, value) {
        return value.replace(/[^a-z0-9]+/gi, '').replace(/(.{4})/g, '$1 ').trim();
    });
});
$(document).on('copy cut paste', '.cardNumber', function() {
    setTimeout(function() {
        $('.cardNumber').trigger('change');
    });
});

HTML

<div class='credit-card-validation'>
    <p>
    <label>Card Number</label>
    <input type="tel" name="cardNumber" class="cardNumber" maxlength="16" placeholder="0000 0000 0000 0000" data-validation-type="custom" data-validation-error-msg="Please enter a valid card number" data-validation-error-msg-container="#cardnumber-error-dialog">
    <div id="cardnumber-error-dialog" class="field-error"></div>
    </p>

    <p>
        <label>Expiry Date</label>
        <input type="text" name="cardExpiry" maxlength="5" class="cardExpiry" placeholder="mm/yy" data-validation-type="alphanumeric" data-validation-error-msg="Please enter a valid card expiry" data-validation-error-msg-container="#cardexpiry-error-dialog">
        <div id="cardexpiry-error-dialog" class="field-error"></div>
    </p>

    <p>
        <label>CVV Code</label>
        <input type="text" name="cardCVV" maxlength="3" class="cardCVV" data-validation-type="numeric" data-validation-error-msg="Please enter a valid CVV number" data-validation-error-msg-container="#cardcvv-error-dialog">
        <div id="cardcvv-error-dialog" class="field-error"></div>
    </p>
</div>

Note that this validation snippet comes from a custom solution implemented for a custom project. You will need CSS styling for all classes mentioned above. Feel free to customise it as you see fit and, if you wish, share the code.

Find more JavaScript tutorials, code snippets and samples here or more jQuery tutorials, code snippets and samples here.

Regular Expressions for the most used debit/credit cards

Title Visa Credit Card
Expression (^4\d{12}$)|(^4[0-8]\d{14}$)|(^(49)[^013]\d{13}$)|(^(49030)[0-1]\d{10}$)|(^(49033)[0-4]\d{10}$)|(^(49110)[^12]\d{10}$)|(^(49117)[0-3]\d{10}$)|(^(49118)[^0-2]\d{10}$)|(^(493)[^6]\d{12}$)
Description Matches Visa Credit Card types 13 or 16 digits starting with 4 including Visa Credit Card, Visa ATM only, Visa Electron and Visa Delta. Safe for international and will NOT match “^(49)” Switch debit cards.
Matches 4111111111111111
Non-Matches 4903020000000008
Title Maestro Credit Card
Expression (^(5[0678])\d{11,18}$) |(^(6[^0357])\d{11,18}$) |(^(601)[^1]\d{9,16}$) |(^(6011)\d{9,11}$) |(^(6011)\d{13,16}$) |(^(65)\d{11,13}$) |(^(65)\d{15,18}$) |(^(633)[^34](\d{9,16}$)) |(^(6333)[0-4](\d{8,10}$)) |(^(6333)[0-4](\d{12}$)) |(^(6333)[0-4](\d{15}$)) |(^(6333)[5-9](\d{8,10}$)) |(^(6333)[5-9](\d{12}$)) |(^(6333)[5-9](\d{15}$)) |(^(6334)[0-4](\d{8,10}$)) |(^(6334)[0-4](\d{12}$)) |(^(6334)[0-4](\d{15}$)) |(^(67)[^(59)](\d{9,16}$)) |(^(6759)](\d{9,11}$)) |(^(6759)](\d{13}$)) |(^(6759)](\d{16}$)) |(^(67)[^(67)](\d{9,16}$)) |(^(6767)](\d{9,11}$)) |(^(6767)](\d{13}$)) |(^(6767)](\d{16}$))
Description Maestro ONLY card matching – begins with “50” or “56-58” or “6” & 13 to 20 total digits. Overlap of Discover, Solo and Switch is handled and NOT matched.
Matches 5600000000000003
Non-Matches 6011000000000004
Title MasterCard Credit Card
Expression ^5[1-5]\d{14}$
Description Matches MasterCard credit cards. International safe, no overlap with other cards.
Matches 5100000000000008
Non-Matches 201400000000009
Title Discover Credit Card
Expression (^(6011)\d{12}$)|(^(65)\d{14}$)
Description Discover Credit Card matching. International safe, no overlap with other cards.
Matches 6011000000000004
Non-Matches 4508750000000009
Title AMEX Credit Card
Expression (^3[47])((\d{11}$)|(\d{13}$))
Description AMEX Credit Card match. International safe.
Matches 340000000000009
Non-Matches 4508750000000009
Title Solo Credit card
Expression (^(6334)[5-9](\d{11}$|\d{13,14}$)) |(^(6767)(\d{12}$|\d{14,15}$))
Description Solo Credit Card match. International safe.
Matches 6334500000000003
Non-Matches 3528000000000007
Title Switch Credit Card
Expression (^(49030)[2-9](\d{10}$|\d{12,13}$)) |(^(49033)[5-9](\d{10}$|\d{12,13}$)) |(^(49110)[1-2](\d{10}$|\d{12,13}$)) |(^(49117)[4-9](\d{10}$|\d{12,13}$)) |(^(49118)[0-2](\d{10}$|\d{12,13}$)) |(^(4936)(\d{12}$|\d{14,15}$)) |(^(564182)(\d{11}$|\d{13,14}$)) |(^(6333)[0-4](\d{11}$|\d{13,14}$)) |(^(6759)(\d{12}$|\d{14,15}$))
Description Switch Credit Card match. International safe.
Matches 4903020000000008
Non-Matches 4111111111111111
Title JCB credit card
Expression (^(352)[8-9](\d{11}$|\d{12}$))|(^(35)[3-8](\d{12}$|\d{13}$))
Description JCB Credit Card match. International safe.
Matches 3528000000000007
Non-Matches 4508750000000009
Title Dinner credit card
Expression (^(30)[0-5]\d{11}$)|(^(36)\d{12}$)|(^(38[0-8])\d{11}$)
Description Diners Credit Card match. International safe.
Matches 30000000000004
Non-Matches 3528000000000007
Title Cart Blanche Credit Card
Expression ^(389)[0-9]{11}$
Description Cart Blanche Credit Card match. International safe.
Matches 38900000000007
Non-Matches 30000000000004
Title EnRoute Credit Card
Expression (^(2014)|^(2149))\d{11}$
Description EnRoute Credit Card match. international safe.
Matches 201400000000009
Non-Matches 38900000000007
Title UK Debit Cards
Expression (^(5[0678])\d{11,18}$)|(^(6[^05])\d{11,18}$)|(^(601)[^1]\d{9,16}$)|(^(6011)\d{9,11}$)|(^(6011)\d{13,16}$)|(^(65)\d{11,13}$)|(^(65)\d{15,18}$)|(^(49030)[2-9](\d{10}$|\d{12,13}$))|(^(49033)[5-9](\d{10}$|\d{12,13}$))|(^(49110)[1-2](\d{10}$|\d{12,13}$))|(^(49117)[4-9](\d{10}$|\d{12,13}$))|(^(49118)[0-2](\d{10}$|\d{12,13}$))|(^(4936)(\d{12}$|\d{14,15}$))
Description Matches any of Solo, Switch or Maestro. International safe. Will not match on Discover, Visa or Mastercard. For example; Most recommendations for matching Visa are that they start with “4”. If outside the US, this are incomplete on will falsly ID most Switch cards as Visa.
Matches 6334500000000003
Non-Matches 6011000000000004

Related posts