payment-cards.js provides formatting, validation, and brand detection for payment card forms.
Dependencies: none (not even jQuery).
Compatibility: obsolete browsers are not supported. For example IE ≤ 8 are not, because they don't have addEventListener.
Maturity: payment-cards.js is stable and has been used in production.
Maintenance: we haven't been using payment-cards.js since we switched to Stripe, so it's not actively maintained by us. However, we still accept and review pull requests.
You can test payment-cards.js on https://liberapay.github.io/payment-cards.js/.
Due to the lack of accurate and comprehensive public information, the validation of card data and the detection of a card's brand should not be blindly trusted.
Validity checks are only meant to help detect input errors early: you should warn your users when the data they have input appears to be invalid, but you should not prevent them from submitting that data.
Terminology:
- PAN = Primary Account Number - the main number visible on the card, commonly called "the card number" - https://en.wikipedia.org/wiki/ISO/IEC_7812
- CVN = Card Verification Number - https://en.wikipedia.org/wiki/Card_security_code
- IIN = Issuer Identification Number - the first 6 digits of a PAN - https://en.wikipedia.org/wiki/Issuer_identification_number
This is the class that glues everything together, it's the most convenient way to use this library.
var cardForm = new PaymentCards.Form(
document.querySelector('input#pan'),
document.querySelector('input#expiry'),
document.querySelector('input#cvn')
);If you want to do something with the inputs, like attaching extra event callbacks, you can access them through the inputs attribute: cardForm.inputs.pan, cardForm.inputs.expiry, cardForm.inputs.cvn.
When you want to check the card data, call the check() method:
var card = cardForm.check();The returned object has the following structure:
{
pan: Object // the value and validation status of the PAN
expiry: Object // the value and validation status of the expiration date
cvn: Object // the value and validation status of the CVN
range: Object or null // the detected range of the card, or null if the IIN is unknown, see 'rangesArray' for details
brand: String or null // shortcut for 'range.brand'
}The pan, expiry, and cvn objects all share the same structure:
{
value: String // the non-formatted value of the field
status: String or null
description: String or undefined
subfield: String or undefined
}status is one of:
'valid': the value appears to be valid'empty': the<input>doesn't contain any digits'abnormal': the value appears to be invalid, it will probably be rejected if you attempt a payment'invalid': the value is invalid, it will almost certainly be rejected if you attempt a paymentnull: the validity is unclear, because the IIN is unknown
description only appears when status is 'abnormal' or 'invalid', it tells you in what way the data is wrong. Possible values include:
"too short", e.g. a CVN of less than 3 digits"too long", e.g. a PAN of more than 19 digits"bad length", e.g. a PAN of 11 digits (to our knowledge no institution issues such numbers)"luhn check failure", the PAN's last digit failed the Luhn check"in the past", for the expiration date only
Obviously these are not meant to be shown to the user directly.
subfield only appears when a specific part of the expiration date is invalid, it's either 'month' or 'year'.
rangesArray contains the data used to guess a card's brand. Each item has the following structure:
{
brand: String // the brand's name, see below for the values
pattern: RegExp // the regular expression matching the IINs of this range, e.g. /^4/ for Visa
spacing: Array // a list of integers indicating where to put spaces when formatting a PAN
panLengths: Array // the list of normal PAN lengths for this range
cvnLengths: Array // the list of normal CVN lengths for this range
}The current brand strings are:
American ExpressDiners ClubDiscoverJCBMaestroMasterCardUnionPayVisa
We currently have two spacing arrays:
- the default one is
[4, 8, 12], i.e. spaces after the 4th, 8th, and 12th digits- example: 4242424242424242 → 4242 4242 4242 4242
- the other one is
[4, 10], for American Express and Diners Club cards- example: 34343434343434 → 3434 343434 3434
If you've seen a card with more than 16 digits or with different spacing, let us know.
PaymentCards.addSeparators(string, positions, separator)PaymentCards.checkCard(pan, expiry, cvn)PaymentCards.checkExpiry(expiry)PaymentCards.formatInputs(panInput, expiryInput, cvnInput)PaymentCards.getSpacing(cardNumber)PaymentCards.getRange(cardNumber)PaymentCards.luhnCheck(num)PaymentCards.restrictNumeric(input, maxLength, formatter)
Note: unlike some other implementations we've seen, our restrictNumeric function does not prevent the user from pasting into the restricted <input>, nor from using text selection to modify its content.
- https://github.com/jessepollak/card
- "Make your credit card form better in one line of code"
- This cool project attempts to show the user a realistic facsimile of their physical card, using the IIN to guess its brand and thus its appearance.
- https://github.com/jessepollak/payment
- "A jQuery-free general purpose library for building credit card forms, validating inputs and formatting numbers"
- This is the code underlying the above project, by the same author.
- https://github.com/PawelDecowski/jquery-creditcardvalidator
- "jQuery credit card validation and detection plugin"
- If you prefer a jQuery plugin this one looks okay (we haven't tried it).
All three of these projects use CoffeeScript, and jQuery or the minimal jQuery replica QJ. On the other hand payment-cards.js is written in light standard JavaScript.