//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.validators { import mx.managers.ISystemManager; import mx.managers.SystemManager; import mx.resources.IResourceManager; import mx.resources.ResourceManager; import mx.utils.StringUtil; import mx.validators.IValidatorListener; [ResourceBundle("validators")] /** * The CreditCardValidator class validates that a credit card number * is the correct length, has the correct prefix, and passes * the Luhn mod10 algorithm for the specified card type. * This validator does not check whether the credit card * is an actual active credit card account. * *
You can specify the input to the CreditCardValidator in two ways:
*cardNumberSource
and
* cardNumberProperty
properties to specify
* the location of the credit card number, and the
* cardTypeSource
and cardTypeProperty
properties
* to specify the location of the credit card type to validate.source
and
* property
properties to specify a single Object.
* The Object should contain the following fields:
* cardType
- Specifies the type
* of credit card being validated.
* In MXML, use the values: "American Express"
,
* "Diners Club"
, "Discover"
,
* "MasterCard"
, or "Visa"
.
In ActionScript, use the static constants
* CreditCardValidatorCardType.MASTER_CARD
,
* CreditCardValidatorCardType.VISA
, or
* CreditCardValidatorCardType.AMERICAN_EXPRESS
* CreditCardValidatorCardType.DISCOVER
, or
* CreditCardValidatorCardType.DINERS_CLUB
.
cardNumber
- Specifies the number of the card
* being validated.To perform the validation, it uses the following guidelines:
*Length:
*The <mx:CreditCardValidator>
tag
* inherits all of the tag attributes of its superclass,
* and adds the following tag attributes:
* <mx:CreditCardValidator * allowedFormatChars=" -" * cardNumberListener="Object specified by cardNumberSource" * cardNumberProperty="No default" * cardNumberSource="No default" * cardTypeListener="Object specified by cardTypeSource" * cardTypeProperty="No default" * cardTypeSource="No default" * invalidCharError= "Invalid characters in your credit card number. (Enter numbers only.)" * invalidNumberError="The credit card number is invalid." * noNumError="No credit card number is specified." * noTypeError="No credit card type is specified or the type is not valid." * wrongLengthError="Your credit card number contains the wrong number of digits." * wrongTypeError="Incorrect card type is specified." * /> ** * @see mx.validators.CreditCardValidatorCardType * * @includeExample examples/CreditCardValidatorExample.mxml * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class CreditCardValidator extends Validator { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- /** * Convenience method for calling a validator. * Each of the standard Flex validators has a similar convenience method. * * @param validator The CreditCardValidator instance. * * @param value A field to validate, which must contain * the following fields: *
cardType
- Specifies the type of credit card being validated.
* Use the static constants
* CreditCardValidatorCardType.MASTER_CARD
,
* CreditCardValidatorCardType.VISA
,
* CreditCardValidatorCardType.AMERICAN_EXPRESS
,
* CreditCardValidatorCardType.DISCOVER
, or
* CreditCardValidatorCardType.DINERS_CLUB
.cardNumber
- Specifies the number of the card
* being validated.value
parameter
* specifies value.date, the baseField
value is "date".
*
* @return An Array of ValidationResult objects, with one ValidationResult
* object for each field examined by the validator.
*
* @see mx.validators.ValidationResult
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static function validateCreditCard(validator:CreditCardValidator,
value:Object,
baseField:String):Array
{
var results:Array = [];
// Resource-backed properties of the validator.
var allowedFormatChars:String = validator.allowedFormatChars;
var resourceManager:IResourceManager = ResourceManager.getInstance();
var baseFieldDot:String = baseField ? baseField + "." : "";
var valid:String = DECIMAL_DIGITS + allowedFormatChars;
var cardType:String = null;
var cardNum:String = null;
var digitsOnlyCardNum:String = "";
var message:String;
var n:int;
var i:int;
try
{
cardType = String(value.cardType);
}
catch(e:Error)
{
// Use the default value and move on
message = resourceManager.getString(
"validators", "missingCardType");
throw new Error(message);
}
try
{
cardNum = value.cardNumber;
}
catch(f:Error)
{
// Use the default value and move on
message = resourceManager.getString(
"validators", "missingCardNumber");
throw new Error(message);
}
if (validator.required)
{
if (cardType.length == 0)
{
results.push(new ValidationResult(
true, baseFieldDot + "cardType",
"requiredField", validator.requiredFieldError));
}
if (!cardNum)
{
results.push(new ValidationResult(
true, baseFieldDot + "cardNumber",
"requiredField", validator.requiredFieldError));
}
}
n = allowedFormatChars.length;
for (i = 0; i < n; i++)
{
if (DECIMAL_DIGITS.indexOf(allowedFormatChars.charAt(i)) != -1)
{
message = resourceManager.getString(
"validators", "invalidFormatChars");
throw new Error(message);
}
}
if (!cardType)
{
results.push(new ValidationResult(
true, baseFieldDot + "cardType",
"noType", validator.noTypeError));
}
else if (cardType != CreditCardValidatorCardType.MASTER_CARD &&
cardType != CreditCardValidatorCardType.VISA &&
cardType != CreditCardValidatorCardType.AMERICAN_EXPRESS &&
cardType != CreditCardValidatorCardType.DISCOVER &&
cardType != CreditCardValidatorCardType.DINERS_CLUB)
{
results.push(new ValidationResult(
true, baseFieldDot + "cardType",
"wrongType", validator.wrongTypeError));
}
if (!cardNum)
{
results.push(new ValidationResult(
true, baseFieldDot + "cardNumber",
"noNum", validator.noNumError));
}
if (cardNum)
{
n = cardNum.length;
for (i = 0; i < n; i++)
{
var temp:String = "" + cardNum.substring(i, i + 1);
if (valid.indexOf(temp) == -1)
{
results.push(new ValidationResult(
true, baseFieldDot + "cardNumber",
"invalidChar", validator.invalidCharError));
}
if (DECIMAL_DIGITS.indexOf(temp) != -1)
digitsOnlyCardNum += temp;
}
}
if (results.length > 0)
return results;
var cardNumLen:int = digitsOnlyCardNum.toString().length;
var correctLen:Number = -1;
var correctLen2:Number = -1;
var correctPrefixArray:Array = [];
// diner club cards with a beginning digit of 5 need to be treated as
// master cards. Go to the following link for more info.
// http://www.globalpaymentsinc.com/myglobal/industry_initiatives/mc-dc-canada.html
if (cardType == CreditCardValidatorCardType.DINERS_CLUB &&
digitsOnlyCardNum.charAt(0) == "5")
{
cardType = CreditCardValidatorCardType.MASTER_CARD;
}
switch (cardType)
{
case CreditCardValidatorCardType.MASTER_CARD:
{
correctLen = 16;
correctPrefixArray.push("51");
correctPrefixArray.push("52");
correctPrefixArray.push("53");
correctPrefixArray.push("54");
correctPrefixArray.push("55");
break;
}
case CreditCardValidatorCardType.VISA:
{
correctLen = 13;
correctLen2 = 16;
correctPrefixArray.push("4");
break;
}
case CreditCardValidatorCardType.AMERICAN_EXPRESS:
{
correctLen = 15;
correctPrefixArray.push("34");
correctPrefixArray.push("37");
break;
}
case CreditCardValidatorCardType.DISCOVER:
{
correctLen = 16;
correctPrefixArray.push("6011");
break;
}
case CreditCardValidatorCardType.DINERS_CLUB:
{
correctLen = 14;
correctPrefixArray.push("300");
correctPrefixArray.push("301");
correctPrefixArray.push("302");
correctPrefixArray.push("303");
correctPrefixArray.push("304");
correctPrefixArray.push("305");
correctPrefixArray.push("36");
correctPrefixArray.push("38");
break;
}
default:
{
results.push(new ValidationResult(
true, baseFieldDot + "cardType",
"wrongType", validator.wrongTypeError));
return results;
}
}
if ((cardNumLen != correctLen) && (cardNumLen != correctLen2))
{
results.push(new ValidationResult(
true, baseFieldDot + "cardNumber",
"wrongLength", validator.wrongLengthError));
return results;
}
// Validate the prefix
var foundPrefix:Boolean = false;
for (i = correctPrefixArray.length - 1; i >= 0; i--)
{
if (digitsOnlyCardNum.indexOf(correctPrefixArray[i]) == 0)
{
foundPrefix = true;
break;
}
}
if (!foundPrefix)
{
results.push(new ValidationResult(
true, baseFieldDot + "cardNumber",
"invalidNumber", validator.invalidNumberError));
return results;
}
// Implement Luhn formula testing of this.cardNumber
var doubledigit:Boolean = false;
var checkdigit:int = 0;
var tempdigit:int;
for (i = cardNumLen - 1; i >= 0; i--)
{
tempdigit = Number(digitsOnlyCardNum.charAt(i));
if (doubledigit)
{
tempdigit *= 2;
checkdigit += (tempdigit % 10);
if ((tempdigit / 10) >= 1.0)
checkdigit++;
doubledigit = false;
}
else
{
checkdigit = checkdigit + tempdigit;
doubledigit = true;
}
}
if ((checkdigit % 10) != 0)
{
results.push(new ValidationResult(
true, baseFieldDot + "cardNumber",
"invalidNumber", validator.invalidNumberError));
return results;
}
return results;
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function CreditCardValidator()
{
super();
subFields = [ "cardNumber", "cardType" ];
}
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// actualListeners
//----------------------------------
/**
* @private
* Returns either the listener or the source
* for the cardType and cardNumber subfields.
*/
override protected function get actualListeners():Array
{
var results:Array = [];
var typeResult:Object;
if (_cardTypeListener)
typeResult = _cardTypeListener;
else if (_cardTypeSource)
typeResult = _cardTypeSource;
results.push(typeResult);
if (typeResult is IValidatorListener)
IValidatorListener(typeResult).validationSubField = "cardType";
var numResult:Object;
if (_cardNumberListener)
numResult = _cardNumberListener;
else if (_cardNumberSource)
numResult = _cardNumberSource;
results.push(numResult);
if (numResult is IValidatorListener)
IValidatorListener(numResult).validationSubField = "cardNumber";
if (results.length > 0 && listener)
results.push(listener);
return results;
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// allowedFormatChars
//----------------------------------
/**
* @private
* Storage for the allowedFormatChars property.
*/
private var _allowedFormatChars:String;
/**
* @private
*/
private var allowedFormatCharsOverride:String;
[Inspectable(category="General", defaultValue="null")]
/**
* The set of formatting characters allowed in the
* cardNumber
field.
*
* @default " -" (space and dash)
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get allowedFormatChars():String
{
return _allowedFormatChars;
}
/**
* @private
*/
public function set allowedFormatChars(value:String):void
{
if (value != null)
{
var n:int = value.length;
for (var i:int = 0; i < n; i++)
{
if (DECIMAL_DIGITS.indexOf(value.charAt(i)) != -1)
{
var message:String = resourceManager.getString(
"validators", "invalidFormatChars");
throw new Error(message);
}
}
}
allowedFormatCharsOverride = value;
_allowedFormatChars = value != null ?
value :
resourceManager.getString(
"validators",
"creditCardValidatorAllowedFormatChars");
}
//----------------------------------
// cardNumberListener
//----------------------------------
/**
* @private
* Storage for the cardNumberListener property.
*/
private var _cardNumberListener:IValidatorListener;
[Inspectable(category="General")]
/**
* The component that listens for the validation result
* for the card number subfield.
* If none is specified, use the value specified
* to the cardNumberSource
property.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get cardNumberListener():IValidatorListener
{
return _cardNumberListener;
}
/**
* @private
*/
public function set cardNumberListener(value:IValidatorListener):void
{
if (_cardNumberListener == value)
return;
removeListenerHandler();
_cardNumberListener = value;
addListenerHandler();
}
//----------------------------------
// cardNumberProperty
//----------------------------------
[Inspectable(category="General")]
/**
* Name of the card number property to validate.
* This attribute is optional, but if you specify
* the cardNumberSource
property,
* you should also set this property.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var cardNumberProperty:String;
//----------------------------------
// cardNumberSource
//----------------------------------
/**
* @private
* Storage for the cardNumberSource property.
*/
private var _cardNumberSource:Object;
[Inspectable(category="General")]
/**
* Object that contains the value of the card number field.
* If you specify a value for this property, you must also specify
* a value for the cardNumberProperty
property.
* Do not use this property if you set the source
* and property
properties.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get cardNumberSource():Object
{
return _cardNumberSource;
}
/**
* @private
*/
public function set cardNumberSource(value:Object):void
{
if (_cardNumberSource == value)
return;
if (value is String)
{
var message:String = resourceManager.getString(
"validators", "CNSAttribute", [ value ]);
throw new Error(message);
}
removeListenerHandler();
_cardNumberSource = value;
addListenerHandler();
}
//----------------------------------
// cardTypeListener
//----------------------------------
/**
* @private
* Storage for the cardTypeListener property.
*/
private var _cardTypeListener:IValidatorListener;
[Inspectable(category="General")]
/**
* The component that listens for the validation result
* for the card type subfield.
* If none is specified, then use the value
* specified to the cardTypeSource
property.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get cardTypeListener():IValidatorListener
{
return _cardTypeListener;
}
/**
* @private
*/
public function set cardTypeListener(value:IValidatorListener):void
{
if (_cardTypeListener == value)
return;
removeListenerHandler();
_cardTypeListener = value;
addListenerHandler();
}
//----------------------------------
// cardTypeProperty
//----------------------------------
[Inspectable(category="General")]
/**
* Name of the card type property to validate.
* This attribute is optional, but if you specify the
* cardTypeSource
property,
* you should also set this property.
*
* In MXML, valid values are:
*"American Express"
"Diners Club"
"Discover"
"MasterCard"
"Visa"
In ActionScript, you can use the following constants to set this property:
*CreditCardValidatorCardType.AMERICAN_EXPRESS
,
* CreditCardValidatorCardType.DINERS_CLUB
,
* CreditCardValidatorCardType.DISCOVER
,
* CreditCardValidatorCardType.MASTER_CARD
, and
* CreditCardValidatorCardType.VISA
.
cardTypeProperty
property.
* Do not use this property if you set the source
* and property
properties.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get cardTypeSource():Object
{
return _cardTypeSource;
}
/**
* @private
*/
public function set cardTypeSource(value:Object):void
{
if (_cardTypeSource == value)
return;
if (value is String)
{
var message:String = resourceManager.getString(
"validators", "CTSAttribute", [ value ]);
throw new Error(message);
}
removeListenerHandler();
_cardTypeSource = value;
addListenerHandler();
}
//--------------------------------------------------------------------------
//
// Properties: Errors
//
//--------------------------------------------------------------------------
//----------------------------------
// invalidCharError
//----------------------------------
/**
* @private
* Storage for the invalidCharError property.
*/
private var _invalidCharError:String;
/**
* @private
*/
private var invalidCharErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the cardNumber
field contains invalid characters.
*
* @default "Invalid characters in your credit card number. (Enter numbers only.)"
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get invalidCharError():String
{
return _invalidCharError;
}
/**
* @private
*/
public function set invalidCharError(value:String):void
{
invalidCharErrorOverride = value;
_invalidCharError = value != null ?
value :
resourceManager.getString(
"validators", "invalidCharErrorCCV");
}
//----------------------------------
// invalidNumberError
//----------------------------------
/**
* @private
* Storage for the invalidNumberError property.
*/
private var _invalidNumberError:String;
/**
* @private
*/
private var invalidNumberErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the credit card number is invalid.
*
* @default "The credit card number is invalid."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get invalidNumberError():String
{
return _invalidNumberError;
}
/**
* @private
*/
public function set invalidNumberError(value:String):void
{
invalidNumberErrorOverride = value;
_invalidNumberError = value != null ?
value :
resourceManager.getString(
"validators", "invalidNumberError");
}
//----------------------------------
// noNumError
//----------------------------------
/**
* @private
* Storage for the noNumError property.
*/
private var _noNumError:String;
/**
* @private
*/
private var noNumErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the cardNumber
field is empty.
*
* @default "No credit card number is specified."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get noNumError():String
{
return _noNumError;
}
/**
* @private
*/
public function set noNumError(value:String):void
{
noNumErrorOverride = value;
_noNumError = value != null ?
value :
resourceManager.getString(
"validators", "noNumError");
}
//----------------------------------
// noTypeError
//----------------------------------
/**
* @private
* Storage for the noTypeError property.
*/
private var _noTypeError:String;
/**
* @private
*/
private var noTypeErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the cardType
field is blank.
*
* @default "No credit card type is specified or the type is not valid."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get noTypeError():String
{
return _noTypeError;
}
/**
* @private
*/
public function set noTypeError(value:String):void
{
noTypeErrorOverride = value;
_noTypeError = value != null ?
value :
resourceManager.getString(
"validators", "noTypeError");
}
//----------------------------------
// wrongLengthError
//----------------------------------
/**
* @private
* Storage for the wrongLengthError property.
*/
private var _wrongLengthError:String;
/**
* @private
*/
private var wrongLengthErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the cardNumber
field contains the wrong
* number of digits for the specified credit card type.
*
* @default "Your credit card number contains the wrong number of digits."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get wrongLengthError():String
{
return _wrongLengthError;
}
/**
* @private
*/
public function set wrongLengthError(value:String):void
{
wrongLengthErrorOverride = value;
_wrongLengthError = value != null ?
value :
resourceManager.getString(
"validators", "wrongLengthErrorCCV");
}
//----------------------------------
// wrongTypeError
//----------------------------------
/**
* @private
* Storage for the wrongTypeError property.
*/
private var _wrongTypeError:String;
/**
* @private
*/
private var wrongTypeErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message the cardType
field contains an invalid credit card type.
* You should use the predefined constants for the cardType
field:
* CreditCardValidatorCardType.MASTER_CARD
,
* CreditCardValidatorCardType.VISA
,
* CreditCardValidatorCardType.AMERICAN_EXPRESS
,
* CreditCardValidatorCardType.DISCOVER
, or
* CreditCardValidatorCardType.DINERS_CLUB
.
*
* @default "Incorrect card type is specified."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get wrongTypeError():String
{
return _wrongTypeError;
}
/**
* @private
*/
public function set wrongTypeError(value:String):void
{
wrongTypeErrorOverride = value;
_wrongTypeError = value != null ?
value :
resourceManager.getString(
"validators", "wrongTypeError");
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function resourcesChanged():void
{
super.resourcesChanged();
allowedFormatChars = allowedFormatCharsOverride;
invalidCharError = invalidCharErrorOverride;
invalidNumberError = invalidNumberErrorOverride;
noNumError = noNumErrorOverride;
noTypeError = noTypeErrorOverride;
wrongLengthError = wrongLengthErrorOverride;
wrongTypeError = wrongTypeErrorOverride;
}
/**
* Override of the base class doValidation()
method
* to validate a credit card number.
*
* You do not call this method directly; * Flex calls it as part of performing a validation. * If you create a custom Validator class, you must implement this method.
* * @param value an Object to validate. * * @return An Array of ValidationResult objects, with one ValidationResult * object for each field examined by the validator. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function doValidation(value:Object):Array { var results:Array = super.doValidation(value); // Return if there are errors // or if the required property is set to false and length is 0. var val:String = value ? String(value) : ""; if (results.length > 0 || ((val.length == 0) && !required)) return results; else return CreditCardValidator.validateCreditCard(this, value, null); } /** * @private * Grabs the data for the validator from two different sources */ override protected function getValueFromSource():Object { var useValue:Boolean = false; var value:Object = {}; if (cardTypeSource && cardTypeProperty) { value.cardType = cardTypeSource[cardTypeProperty]; useValue = true; } if (cardNumberSource && cardNumberProperty) { value.cardNumber = cardNumberSource[cardNumberProperty]; useValue = true; } if (useValue) return value; else return super.getValueFromSource(); } } }