Jeremy Cook
Jeremy Cook

Reputation: 22063

Should I use the CreditCardAttribute to validate credit card numbers?

Should I use Microsoft's CreditCardAttribute to validate credit card numbers like so?

[Required, CreditCard]
public string CreditCardNumber { get; set; }

Or should I let the payment gateway handle it, or do something else? I ask this after discovering some customers have been unable to submit payment with their credit card information. Fortunately, I was able to work with one of these customers, and found that their Visa card was processed without a problem after removing the CreditCardAttribute.

In part, this question is rhetorical, but I would like to benefit from other developer's thoughts and experiences, and make other developers aware of the risks of using the CreditCardAttribute by asking the question.

Upvotes: 14

Views: 10337

Answers (2)

PaulG
PaulG

Reputation: 14021

In the code behind the credit card attribute, it is simply performing a Luhn check.

All payment cards(*) currently follow ISO/IEC/7812 standard, which has a luhn check digit as the final digit.

This luhn check is simply used to prevent transpositional errors though. It is useful as a sanity check prior to submitting card numbers to a payment gateway, but not suitable to absolutely validate whether a number is a valid card number.

Valid card number ranges change monthly, and the only way to absolutely verify a number is to validate it via a payment gateway. If only attempting to validate a card (rather than charge it) this should be done with a zero value 'authorisation only' style check.

(*) The only exception to this is a card type in China known as China UnionPay
(Historically there was also a Diners Club 'enRoute' brand which was withdrawn in 1992)

Upvotes: 7

Jevgeni Geurtsen
Jevgeni Geurtsen

Reputation: 3133

I think the best way to find this out is to simply test it:

using System;
using System.Linq;
using System.Text;
using System.IO;

namespace SO
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] cards = new string[] {
                //http://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
                "378282246310005",  // American Express
                "4012888888881881", // Visa
                "6011111111111117", // Discover
                "4222222222222", // Visa
                "76009244561", // Dankort (PBS)
                "5019717010103742", // Dakort (PBS) 
                "6331101999990016", // Switch/Solo (Paymentech)
                "30569309025904", // Diners Club 
                //http://www.getcreditcardnumbers.com/
                "5147004213414803", // Mastercard
                "6011491706918120", // Discover
                "379616680189541", // American Express
                "4916111026621797", // Visa
            };

            foreach (string card in cards)
            {
                Console.WriteLine(IsValid(card));
            }

            Console.ReadLine();
        }

        public static bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }

            string ccValue = value as string;
            if (ccValue == null)
            {
                return false;
            }
            ccValue = ccValue.Replace("-", "");
            ccValue = ccValue.Replace(" ", "");

            int checksum = 0;
            bool evenDigit = false;

            // http://www.beachnet.com/~hstiles/cardtype.html
            foreach (char digit in ccValue.Reverse())
            {
                if (digit < '0' || digit > '9')
                {
                    return false;
                }

                int digitValue = (digit - '0') * (evenDigit ? 2 : 1);
                evenDigit = !evenDigit;

                while (digitValue > 0)
                {
                    checksum += digitValue % 10;
                    digitValue /= 10;
                }
            }

            return (checksum % 10) == 0;
        }
    }
}

The IsValid method is from the original C# CreditCardAttribute class. 1 out of the 12 numbers failed:

        True
        True
        True
        True
        False //"76009244561", // Dankort (PBS)
        True
        True
        True
        True
        True
        True
        True

So, should you use it? No, obviously it doesn't detect all numbers. Although you can take their code and improve it!

Upvotes: 5

Related Questions