Do you validate credit cards in your iOS app?


TL;DR

If you need to validate credit card input from your app users, go to CCValidator repo and follow installation and usage guide.


Longer version

Have you ever needed to validate credit card input from user in your app? We have found ourselves in that situation while working on BeforeYouGo app, (the easiest way to buy insurance for your next trip).
We wanted to validate user input on device before it’s sent to our payment provider. It would be simpler to just send ahead everything that’s a number and leave validation to the server, but it would mean more frustration to our users. We didn’t want them to wait until request comes back, only to find out they made an error while typing their card number.

We looked at several different libraries available on GitHub/CocoaPods, but they didn’t fit our needs. Most of them just validate card number using Luhn algorithm (that’s great) and detect card number only after all characters are passed — so they don’t know that 3782 input should be American Express card — they wait until user provides whole number, e.g. 3782 822463 10005. Problem with that, is if your card processor supports e.g. only Visa, you should help your user by telling him already after the first digit, that his Amex card is not supported (Visa cards always start with ‘4’).

After some poking for inspiration, I found a JS lib that was doing exactly what we needed — CreditCardJS. What’s more, it was unique in a sense that it wasn’t using regex for detecting card types, thus making code more readable. See example from their website:

var cardTypes = {
discover: [
[6011],
[622126, 622925],
[644, 649],
[65]
],
visa: [...]
}

The only drawback was — it’s in JS, so I started writing similar tool. First in Obj-C, only to move it later to Swift, when we got an idea to make a Pod out of it. Take a peek at how similar struct looks like in our code:

return (
prefixes:
[CreditCardPrefix(rangeStart: 6011, rangeEnd: 6011, length: 4),
CreditCardPrefix(rangeStart: 622126, rangeEnd: 622925, length: 6),
CreditCardPrefix(rangeStart: 644, rangeEnd: 649, length: 3),
CreditCardPrefix(rangeStart: 65, rangeEnd: 65, length: 2)],
lengths: [16,19]
)
}

Less concise than its JS inspiration, but IMO more readable, without the need to guess what numbers in an array mean.

Full validation

You can find more info about usage of the library in our tests and README. In short — you need to pass to CCValidator class a string representing credit card number. It will be stripped of whitespaces and newlines (also inside the string). As a return value, you will return boolean value showing whether validation was passed or not. Validation process answers following questions:

  1. Was the card type recognized?
  2. Does the length of provided card number match length of recognized credit card?
  3. Does the number pass Luhn algorithm check?

If answer to all these questions is yes , it means card number was validated without problems, if not — validation fails, and you can present an error to your user.

E.g. lets say user inputs 3782 822463 10005 as card number. Looking at the card number prefix, it will be recognized as American Express. Knowing Amex always has 15 digits, we will check if the length is ok. As the last step, number will be passed to a function checking it against Luhn algorithm — in this case it passes it, so the result of the function is true (or YES in Obj-C).

let numberAsString = textField.text
let isFullCardDataOK = CCValidator.validate(creditCardNumber: numberAsString)

Detecting Card Type

Full validation described above requires all card digits (so 15 for Amex, 16 for MasterCard etc.). If your user would provide only first 12 digits of his Amex card, validation would fail, as answer to questions 2) and 3) would be No .

In case you’d like to check card type while user is still typing, (e.g. to let him know early his card is not supported, or to show him his card type using e.g. PaymentFont), we provided a function that takes a string (as short as one character). Based on provided string, validator checks for all known prefixes.

For example for Visa:

CreditCardPrefix(rangeStart: 4, rangeEnd: 4, length: 1)

we check if card prefix — first X characters (in this case first character, as prefix has length == 1) — is in the prefix range (in this case — if it’s equal to number 4). This way, by knowing only a few characters, we can try to recognize card type.

let numberAsString = textField.text
let recognizedType = CCValidator.typeCheckingPrefixOnly(creditCardNumber: numberAsString)
//now you can check if type is e.g. .Visa, .MasterCard or .NotRecognized

Currently, following card types can be recognized:

  • American Express
  • Dankort
  • Diners Club
  • Discover
  • JCB
  • Maestro
  • MasterCard
  • UnionPay
  • VisaElectron
  • Visa

For information about card prefix and expected number length we used mostly information gathered on Wikipedia + some extra googling to double-check some data. For some it’s quite easy to find a lot of info (like on Meastro), for others — it’s more time consuming.

How to contribute

You can find repository on our GitHub page.

If you’d like to see more card types in the library — you’re more than welcome to contribute, by adding another spec for requested card type. You need to modify only one file — CCValidator.swift . When adding new type, you can mimick how we wrote specs for the rest of the types.

If you’re not sure how to modify and contribute, but you know specification for the card type you’d like to add — you can let us know, and we can expand our library after making sure specification is correct.

If you have any other feedback, or comments, please let us know, we’d love to hear from you!