1

I am needing help with a credit card validation script. I have figured out how to determine if a credit card is valid/invalid but what I am needing is this:

If someone enters an invalid credit card number, is there a way to determine which number is invalid?

Here is my current code to determine if a card is valid/invalid:

function validateCreditcard_number($credit_card_number) {
    //Get the first digit
    $firstnumber = substr($credit_card_number, 0, 1);
    //Make sure it is the correct amount of digits. Account for dashes being present.
    switch ($firstnumber) {
        case 3:
            if (!preg_match('/^3\d{3}[ \-]?\d{6}[ \-]?\d{5}$/', $credit_card_number)) {
               return '<li>This is not a valid American Express card number. Please use a different credit/debit card or re-enter your credit/debit card details.</li>';
            }
            break;
        case 4:
            if (!preg_match('/^4\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number)) {
                return '<li>This is not a valid Visa card number. Please use a different credit/debit card or re-enter your credit/debit card details.</li>';
            }
            break;
        case 5:
            if (!preg_match('/^5\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number)) {
                 return '<li>This is not a valid MasterCard card number. Please use a different credit/debit card or re-enter your credit/debit card details.</li>';
            }
            break;
        case 6:
            if (!preg_match('/^6011[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number)) {
                return '<li>This is not a valid Discover card number. Please use a different credit/debit card or re-enter your credit/debit card details.</li>';
            }
            break;
        default:
            return '<li>This is not a valid credit card number. Please use a different credit/debit card or re-enter your credit/debit card details.</li>';
    } //END Switch statement

    //Luhn Algorithm
    $credit_card_number = str_replace('.', '', $credit_card_number);
    $credit_card_number = str_replace('-', '', $credit_card_number);
    $credit_card_number = str_replace(' ', '', $credit_card_number);
    $map = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                0, 2, 4, 6, 8, 1, 3, 5, 7, 9);
    $sum = 0;
    $last = strlen($credit_card_number) - 1;

    for ($i = 0; $i <= $last; $i++) {
        $sum += $map[$credit_card_number[$last - $i] + ($i & 1) * 10];
    }

    if ($sum % 10 != 0) {
       return '<li>This is not a valid credit card number. Please use a different credit/debit card or re-enter your credit/debit card details.</li>';
    } else {
       //If we made it this far the credit card number is in a valid format
       return 'This is a valid credit card number' ;
    }

} //END validateCreditcard_number
user229044
  • 232,980
  • 40
  • 330
  • 338
three3
  • 2,756
  • 14
  • 57
  • 85
  • 2
    This is definitely something that should be handled by the payment gateway API. All that I've worked with have an API call to do this. – Kai Qing Nov 10 '11 at 00:39
  • 11
    Personally, I'd never go down the route of explaining to a user *why* the credit card was invalid. The true owner of a credit card will realise their mistake on their own. Someone trying to get free stuff, will only be egged on by your precise instructions as to what is incorrect. – Moo-Juice Nov 10 '11 at 00:39
  • 5
    Even if you could, why would you want to? – Oliver Charlesworth Nov 10 '11 at 00:39
  • The reason we would want to is because we deal with a lot of people each day that insert invalid credit card numbers when they are trying to purchase something from our website. Instead of entering their correct credit card info in, they call and order over the phone. We would like to eliminate some phone calls. It would be nice if we could tell them which number is incorrect and what to fix. – three3 Nov 10 '11 at 00:43
  • 4
    @three3, there's a big difference between number that meets a valid credit card format and a credit card number that actually works. I agree with Kai - you'd be better off using a payment gateway or other provider to determine whether an account is valid or not. http://stackoverflow.com/questions/174730/what-is-the-best-way-to-validate-a-credit-card-in-php has some good regexp and other tips (albeit in PHP). – JW8 Nov 10 '11 at 00:45
  • Why do `return` and then `break`? Also, why do you think the people who call will actually bother to fix the issue? Some people are just "phone" people. – Jared Farrish Nov 10 '11 at 00:46
  • 6
    @KenWhite You are completely wrong, that isn't how CC #'s work. So many comments about this being insecure. This is a simple checksum to tell whether the CC is ***potentially*** valid. ***If the card isn't real, it doesn't matter.*** The CC still has to hit the payment gateway, the name/expiry/CSC values still have to match. It's trivially easy to generate "valid" credit card numbers. Non-guessable numbers is *not* part of CC security, or the above algorithm would render them completely broken. Think people, and stop commenting on the security of this practice, it's a silly thing to say. – user229044 Nov 10 '11 at 00:51
  • Of course you're right, @meagar. Comment deleted. – Ken White Nov 10 '11 at 00:55
  • 3
    Follow-up comment. No, it is *not* possible to tell which number causes the validation to fail, because any subsequent number can alter the validity of a CC #. IE, if digit 8 is "incorrect", digit 12 could be altered to compensate. There is no "one" digit responsible for making a CC invalid, it's a sum of all the digits. That's the whole point. – user229044 Nov 10 '11 at 00:56
  • Security issues aside, and "Is it possible?" issues aside, I think this is a waste of effort. "This is not a valid [card type] number" is more than enough information to prompt the user to double-check both the selected card type and the number. As long as the validation is flexible in ignoring spaces and dashes it should be fine. – nnnnnn Nov 10 '11 at 01:01
  • It is not a "silly thing to say" at all. It's in the same field as saying "The password is incorrect", when given a username/password pair. It's a *clue*. And I'd love to think that there are people out there who would go "oh, well, crap." and stop. But there are people who kind take slight hints and then work with that hint. It's **NOT** silly to suggest that pre-validating that card prior to the gateway and indicating a clue as to it's origin might well lead to insecurity. Just say it's invalid, or let the payment gateway deal with it. – Moo-Juice Nov 10 '11 at 01:46
  • 1
    The language is getting a little too personal. As for the security issue, note that three3 doesn't appear to want to inform the user which digits are incorrect when the payment gateway refuses the transaction, but only when the local checks (the issuing network check and Luhm checksum) fail. This is a check that anyone can run for themselves, independent of three3's site, which is why it doesn't constitute a vulnerability. – outis Nov 10 '11 at 04:02
  • @meagar, there are ways of debating and your are out of line in your "context". I know for a fact what a good thief can do. I suggest we agree to disagree. – Moo-Juice Nov 10 '11 at 04:28
  • 1
    @Moo-Juice I'm sorry, but no. I'm here to tell you this *is not an attack vector*. During registration, if *my* system says an email address is invalid, and *your* system says *which character* of an email address is invalid, is one more secure than the other? **NO**, because after the purely cosmetic check of the email, we'll **actually validate it** by **sending an email**. It is *exactly* the same with CC processing. The up-front Luhn check is *not* a security thing, it has nothing to do with security, **it cannot affect security**. End of story. – user229044 Nov 10 '11 at 04:39

3 Answers3

2

You cannot. The credit card numbers have a checksum (error checking code) which allows detecting errors, but they do not have any error correction code.

O'Rooney
  • 2,878
  • 2
  • 27
  • 41
  • The wikipedia article [Bank Card Number](http://en.wikipedia.org/wiki/Bank_card_number) lists the valid lengths, IIN numbers, as well the the validation ("error detection") used, if any. –  Nov 10 '11 at 00:57
2

The Luhn checksum can't detect which digit is incorrect, it can only detect single-digit errors and some multi-digit errors. It's entirely possible to mistype two digits and get a number with a valid checksum.

outis
  • 75,655
  • 22
  • 151
  • 221
2

You could go a long way by not assuming the first number is correct. As it stands, somebody who typos the very first number of their Visa as a 3 instead of a 4 gets told they're entering an invalid American Express. That's brutal, from a usability stand point. If the credit card number is invalid, it's simply invalid. It's not an invalid American Express unless they explicitly choose American Express from another drop-down.

Besides that small improvement, what you ask is impossible. The mod10 checksum algorithm cannot tell you which digit is incorrect.

By way of trying to explain why, consider the following simplified scenario:

  • I have 5 digits, I don't know what they are
  • The sum of the 5 digits is 50
  • One of the following digits is off-by-one

Which of the following digits is incorrect?

9 + 11 + 8 + 9 + 12 = 49

It is impossible to tell. It could be any of those digits, any one of them might be off by -1. But that doesn't begin to describe our trouble. I told you that only one digit is wrong. In your situation, you don't know how many are wrong. One digit might be off by -2, and another image might be off by +1. There are literally an infinite number of "wrong" states. Maybe all five numbers are wrong! Maybe the correct 5 numbers are 0 0 0 0 50. This is the problem you face with mod10/Luhn validation. It is absolutely impossible to tell which number/numbers is/are incorrect.

You asked in response to another answer if there is some other algorithm to tell if an individual digit is incorrect; that doesn't make any sense. Luhn is the algorithm, like it or not. The numbers weren't generated to fit any other algorithm, and you don't get to change the algorithm after it's in use by the entire world.


As an unrelated critique, your function shouldn't be returning human-readable error messages/success messages. It is horrible practice, and leads to unmaintainable, unlocalizable apps.

user229044
  • 232,980
  • 40
  • 330
  • 338