View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.validator.routines;
18  
19  import org.apache.commons.validator.routines.checkdigit.CheckDigit;
20  import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit;
21  import java.io.Serializable;
22  import java.util.List;
23  import java.util.ArrayList;
24  
25  /**
26   * Perform credit card validations.
27   *
28   * <p>
29   * By default, all supported card types are allowed.  You can specify which
30   * cards should pass validation by configuring the validation options. For
31   * example,
32   * </p>
33   *
34   * <pre>
35   * <code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
36   * </pre>
37   *
38   * <p>
39   * configures the validator to only pass American Express and Visa cards.
40   * If a card type is not directly supported by this class, you can implement
41   * the CreditCardType interface and pass an instance into the
42   * <code>addAllowedCardType</code> method.
43   * </p>
44   *
45   * <p>
46   * For a similar implementation in Perl, reference Sean M. Burke's
47   * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
48   * More information can be found in Michael Gilleland's essay 
49   * <a href="http://web.archive.org/web/20120614072656/http://www.merriampark.com/anatomycc.htm">Anatomy of Credit Card Numbers</a>.
50   * </p>
51   *
52   * @version $Revision: 1649191 $
53   * @since Validator 1.4
54   */
55  public class CreditCardValidator implements Serializable {
56  
57      private static final long serialVersionUID = 5955978921148959496L;
58  
59      /**
60       * Option specifying that no cards are allowed.  This is useful if
61       * you want only custom card types to validate so you turn off the
62       * default cards with this option.
63       *
64       * <pre>
65       * <code>
66       * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
67       * v.addAllowedCardType(customType);
68       * v.isValid(aCardNumber);
69       * </code>
70       * </pre>
71       */
72      public static final long NONE = 0;
73  
74      /**
75       * Option specifying that American Express cards are allowed.
76       */
77      public static final long AMEX = 1 << 0;
78  
79      /**
80       * Option specifying that Visa cards are allowed.
81       */
82      public static final long VISA = 1 << 1;
83  
84      /**
85       * Option specifying that Mastercard cards are allowed.
86       */
87      public static final long MASTERCARD = 1 << 2;
88  
89      /**
90       * Option specifying that Discover cards are allowed.
91       */
92      public static final long DISCOVER = 1 << 3;
93  
94      /**
95       * Option specifying that Diners cards are allowed.
96       */
97      public static final long DINERS = 1 << 4;
98  
99      /**
100      * The CreditCardTypes that are allowed to pass validation.
101      */
102     private final List cardTypes = new ArrayList();
103 
104     /**
105      * Luhn checkdigit validator for the card numbers.
106      */
107     private static final CheckDigit LUHN_VALIDATOR = LuhnCheckDigit.LUHN_CHECK_DIGIT;
108 
109     /** American Express (Amex) Card Validator */
110     public static final CodeValidator AMEX_VALIDATOR = new CodeValidator("^(3[47]\\d{13})$", LUHN_VALIDATOR);
111 
112     /** Diners Card Validator */
113     public static final CodeValidator DINERS_VALIDATOR = new CodeValidator("^(30[0-5]\\d{11}|3095\\d{10}|36\\d{12}|3[8-9]\\d{12})$", LUHN_VALIDATOR);
114 
115     /** Discover Card regular expressions */
116     private static final RegexValidator DISCOVER_REGEX = new RegexValidator(new String[] {"^(6011\\d{12})$", "^(64[4-9]\\d{13})$", "^(65\\d{14})$"});
117 
118     /** Discover Card Validator */
119     public static final CodeValidator DISCOVER_VALIDATOR = new CodeValidator(DISCOVER_REGEX, LUHN_VALIDATOR);
120 
121     /** Mastercard Card Validator */
122     public static final CodeValidator MASTERCARD_VALIDATOR = new CodeValidator("^(5[1-5]\\d{14})$", LUHN_VALIDATOR);
123 
124     /** Visa Card Validator */
125     public static final CodeValidator VISA_VALIDATOR = new CodeValidator("^(4)(\\d{12}|\\d{15})$", LUHN_VALIDATOR);
126 
127     /**
128      * Create a new CreditCardValidator with default options.
129      */
130     public CreditCardValidator() {
131         this(AMEX + VISA + MASTERCARD + DISCOVER);
132     }
133 
134     /**
135      * Create a new CreditCardValidator with the specified options.
136      * @param options Pass in
137      * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
138      * those are the only valid card types.
139      */
140     public CreditCardValidator(long options) {
141         super();
142 
143         if (isOn(options, VISA)) {
144             this.cardTypes.add(VISA_VALIDATOR);
145         }
146 
147         if (isOn(options, AMEX)) {
148             this.cardTypes.add(AMEX_VALIDATOR);
149         }
150 
151         if (isOn(options, MASTERCARD)) {
152             this.cardTypes.add(MASTERCARD_VALIDATOR);
153         }
154 
155         if (isOn(options, DISCOVER)) {
156             this.cardTypes.add(DISCOVER_VALIDATOR);
157         }
158 
159         if (isOn(options, DINERS)) {
160             this.cardTypes.add(DINERS_VALIDATOR);
161         }
162     }
163 
164     /**
165      * Create a new CreditCardValidator with the specified {@link CodeValidator}s.
166      * @param creditCardValidators Set of valid code validators
167      */
168     public CreditCardValidator(CodeValidator[] creditCardValidators) {
169         if (creditCardValidators == null) {
170             throw new IllegalArgumentException("Card validators are missing");
171         }
172         for (int i = 0; i < creditCardValidators.length; i++) {
173             cardTypes.add(creditCardValidators[i]);
174         }
175     }
176 
177     /**
178      * Checks if the field is a valid credit card number.
179      * @param card The card number to validate.
180      * @return Whether the card number is valid.
181      */
182     public boolean isValid(String card) {
183         if (card == null || card.length() == 0) {
184             return false;
185         }
186         for (int i = 0; i < cardTypes.size(); i++) {
187             CodeValidator type = (CodeValidator)cardTypes.get(i);
188             if (type.isValid(card)) {
189                 return true;
190             }
191         }
192         return false;
193     }
194 
195     /**
196      * Checks if the field is a valid credit card number.
197      * @param card The card number to validate.
198      * @return The card number if valid or <code>null</code>
199      * if invalid.
200      */
201     public Object validate(String card) {
202         if (card == null || card.length() == 0) {
203             return null;
204         }
205         Object result = null;
206         for (int i = 0; i < cardTypes.size(); i++) {
207             CodeValidator type = (CodeValidator)cardTypes.get(i);
208             result = type.validate(card);
209             if (result != null) {
210                 return result ;
211             }
212         }
213         return null;
214 
215     }
216     /**
217      * Tests whether the given flag is on.  If the flag is not a power of 2
218      * (ie. 3) this tests whether the combination of flags is on.
219      *
220      * @param options The options specified.
221      * @param flag Flag value to check.
222      *
223      * @return whether the specified flag value is on.
224      */
225     private boolean isOn(long options, long flag) {
226         return (options & flag) > 0;
227     }
228 
229 }