using System; using System.Collections; using System.Text.RegularExpressions; using System.Web.UI.WebControls; using System.Web.UI; namespace FrostCommerce.Components { /// /// Performs validation of the credit card number on checkout. /// public class CreditCardValidator : BaseValidator { /// /// Credit card type. /// private CardType _cardType; /// /// Credit card textbox /// private TextBox _creditBox; /// /// If we should check to test for length. /// private bool _valid; /// /// Default constructor /// public CreditCardValidator() { _valid = true; _cardType = CardType.All | CardType.Unknown; } protected override bool ControlPropertiesValid() { Control ctrl = FindControl(ControlToValidate); if (ctrl != null) { if (ctrl is TextBox) { _creditBox = (TextBox)ctrl; return (null != _creditBox); } else return false; } else return false; } protected override bool EvaluateIsValid() { if (_valid) { if (IsValidCard(_creditBox.Text)) { return ValidCardNumber(_creditBox.Text); } else { return false; } } else { return ValidCardNumber(_creditBox.Text); } } /// /// Will ensure that the card is a valid length for the card type. If the /// card type isn't recognised it will return true by default. /// /// The CreditCardValidator control also includes a CardTypes property that determines /// what card types should be accepted. If the card isn't recognised, and the Unknown card /// type is in the AcceptedCardTypes property then the DefaultLengthTest value will be /// returned. /// /// Credit card number to check. /// /// Wither the card is of valid length and of credit number for /// the type of card. /// public bool IsValidCard(string CardNumber) { // AMEX if((Regex.IsMatch(CardNumber, "^(34|37)")) && ((_cardType & CardType.Amex) != 0)) return ( 15 == CardNumber.Length); // MasterCard else if((Regex.IsMatch(CardNumber, "^(51|52|53|54|55)")) && ((_cardType & CardType.MasterCard) != 0)) return ( 16 == CardNumber.Length); // Visa else if((Regex.IsMatch(CardNumber, "^(4)")) && ((_cardType & CardType.Visa) != 0)) return ( 13 == CardNumber.Length || 16 == CardNumber.Length); // Diner's Card else if((Regex.IsMatch(CardNumber, "^(300|301|302|303|304|305|36|38)")) && ((_cardType & CardType.DinersClub) != 0)) return ( 14 == CardNumber.Length); // Discover else if((Regex.IsMatch(CardNumber, "^(6011)")) && ((_cardType & CardType.Discover) != 0)) return ( 16 == CardNumber.Length); // Unknown else { if((_cardType & CardType.Unknown) != 0) return true; else return false; } } /// /// Performs a validation of the credit card number using the Luhn's Formula. /// /// Credit Card number to validate. /// /// Return wither its valid or not. /// private static bool ValidCardNumber(string CardNumber) { try { // Contain the numbers ArrayList Numbers = new ArrayList(); // Length of the credit card int CardLength = CardNumber.Length; // Double the value of the alternate digits, starting with the second digit from // the right and for to the left // Now loop starting from the end for(int i = CardLength - 2; i >= 0; i = i - 2) { // Now read the contents at each index Numbers.Add(Int32.Parse(CardNumber[i].ToString()) * 2); } // Will hold the value of the Credit card int CheckSum = 0; // Add the seperate digits of all the products for(int count = 0; count <= Numbers.Count - 1; count++) { // Holds the sum of the digits int holdNumbers = 0; // Check if the current number has more than one digit if((int)Numbers[count] > 9) { int numberLength = ((int) Numbers[count]).ToString().Length; // Add the count to each digit for(int i = 0; i < numberLength; i++) { holdNumbers = holdNumbers + Int32.Parse(((int)Numbers[count]).ToString()[i].ToString()); } } else { // Single digit add it to itself holdNumbers = (int)Numbers[count]; } // Add the num to the total CheckSum = CheckSum + holdNumbers; } // Add all the digits that wasn't double from the right but // start from the most right number with alternating digits int origSum = 0; for(int i = CardLength - 1; i >= 0; i = i -2) { origSum = origSum + Int32.Parse(CardNumber[i].ToString()); } // Perform the final sum, if the sum modulus results in 0 then its validate else its a fake number return (((origSum + CheckSum) % 10) == 0); } catch(Exception) { return false; } } /// /// Should we validate the length of the credit cards? /// public bool ValidateCardType { get { return _valid; } set { _valid = value; } } /// /// String of all the credit cards we accept /// public string AcceptedCardTypes { get { return _cardType.ToString(); } set { _cardType = (CardType)Enum.Parse(typeof(CardType), value, false); } } } }