When developing a web app that needs to process payments, inevitably the requirement for some sort of credit card validation will come up. Recently I worked on an app that used AngularJS on the front-end and had to accept payments. Being a good consultant often means finding high quality off the shelf solutions (bonus points if the solution is open source and an industry standard). In this blog post I will present the solution we used and also cover how credit card validation works and what can be learned from the credit card number itself.
I found a great project on github, Angular Payments, that makes it very easy to add credit card validation to a form. Integrating Angular Payments was simple. Our backend is Rails, so we just added angular-payments.min.js into application.js:
//=require angular-payments.min. Once that is in place, two directives support validation and formatting.
Angular Payments Validation
To validate an input using Angular Payments:
VALIDATION_TYPE can be “card,” “expiry” (expiration date), or “cvc” (card security code). Additionally, a model for the card type can be provided,
payments-type-model="type", and Angular Payments will automatically fill in
$scope.type with the card type (such as Visa, Mastercard, etc) based on the card number alone. Pretty cool!
Angular Payments Formatting
Formatting is also simple with Angular Payments:
FORMATTING_TYPE can also be “card,” “expiry,” or “cvc.” Formatting will take a string of digits like “4111111111111111” and make it “4111 1111 1111 1111”, giving our form a much more polished and professional look.
payments-validate can be brought together into the same input field.
How Credit Card Validation Works
For the app I wrote, it was necessary to validate the card security code, the expiration date, and the credit card number itself.
Card Security Code
The card security code (CSC, also sometimes called CVV or CVC) is located on the back of the card and can be three or four digits long. However not all credit cards have a CSC, so a length of 0 is also fine, and validation can pass by leaving the input field blank. This validation is straightforward, and Angular Payments supports it.
Expiration date (expiry) is another form entry that needs validation, because we do not want to allow users to enter an expiry in the past. Besides when the date was, there are also several different ways people could enter the date. Imagine an expiration date of April, 2019; the user is shown a single input field and needs to enter the month and year. This can be accomplished in several ways:
- The user could type “0” followed by “4” for the month, or simply “4”.
- If the date is in February, “2” is valid but a month of “20” should not be.
- For the year, “19” or “2019” could both be valid.
- Assumptions about the year should be made, for instance a year of “12” should mean 2012 and should fail validation.
- Any combination of the above- 42019, 042019, 419 and 0419 should work
It turns out date validation isn’t trivial. Again, we saved a lot of development time (and time to write tests that cover all cases) by using Angular Payment.
Credit Card Number
It turns out you can learn a lot just from the credit card number itself. In particular, there are several important pieces of information that can be gleaned from the number:
- If the number is definitely invalid (but not necessarily if it’s a valid number)
- The category of the entity that issued the card (e.g., financial, military, petroleum, etc.)
- The issuer identification (Visa, Mastercard, American Express, etc.)
To check whether the card number is definitely invalid, a relatively simple checksum called the Luhn Formula can be applied, as follows:
- From the rightmost digit (the check digit), moving left, double the value of every second digit; if the product of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then sum the digits of the products (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9).
- Take the sum of all the digits.
- If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
If the number passes the Luhn check, it may be valid (though we can’t know for sure). If it fails the check, it’s definitely invalid and you can mark the input field as invalid as well.
The category of the issuer and issuer identification can be calculated from the first digit and first six digits, respectively. “Cracking the Credit Card Code” at Mint.com summarizes the details in a nice graphic:
By taking advantage of the details in the card number, we can provide a nice user experience. Validation can fail if the customer has a typo in their credit card number input, and we can also automatically fill in the card type for them. Conveniently, Angular Payments supports all of this out of box.