//////////////////////////////////////////////////////////////////////////////// // // 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:

* * *

To perform the validation, it uses the following guidelines:

*

Length:

*
    *
  1. Visa: 13 or 16 digits
  2. *
  3. MasterCard: 16 digits
  4. *
  5. Discover: 16 digits
  6. *
  7. American Express: 15 digits
  8. *
  9. Diners Club: 14 digits or 16 digits if it also functions as MasterCard
  10. *
* Prefix: *
    *
  1. Visa: 4
  2. *
  3. MasterCard: 51 to 55
  4. *
  5. Discover: 6011
  6. *
  7. American Express: 34 or 37
  8. *
  9. Diners Club: 300 to 305, 36 or 38, 51 to 55
  10. *
* * @mxml * *

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: * * * @param baseField Text representation of the subfield * specified in the value parameter. * For example, if the 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:

* * *

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.

* * @see mx.validators.CreditCardValidatorCardType * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var cardTypeProperty:String; //---------------------------------- // cardTypeSource //---------------------------------- /** * @private * Storage for the cardTypeSource property. */ private var _cardTypeSource:Object; [Inspectable(category="General")] /** * Object that contains the value of the card type field. * If you specify a value for this property, you must also specify * a value for the 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(); } } }