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);
}
}
}
}