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   * <p>Perform credit card validations.</p>
27   * <p>
28   * By default, all supported card types are allowed.  You can specify which
29   * cards should pass validation by configuring the validation options.  For
30   * example,<br/><code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
31   * configures the validator to only pass American Express and Visa cards.
32   * If a card type is not directly supported by this class, you can implement
33   * the CreditCardType interface and pass an instance into the
34   * <code>addAllowedCardType</code> method.
35   * </p>
36   * For a similar implementation in Perl, reference Sean M. Burke's
37   * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
38   * More information is also available
39   * <a href="http://www.merriampark.com/anatomycc.htm">here</a>.
40   *
41   * @version $Revision: 1227719 $ $Date: 2012-01-05 18:45:51 +0100 (Do, 05 Jan 2012) $
42   * @since Validator 1.4
43   */
44  public class CreditCardValidator implements Serializable {
45  
46      private static final long serialVersionUID = 5955978921148959496L;
47  
48      /**
49       * Option specifying that no cards are allowed.  This is useful if
50       * you want only custom card types to validate so you turn off the
51       * default cards with this option.
52       * <br/>
53       * <pre>
54       * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
55       * v.addAllowedCardType(customType);
56       * v.isValid(aCardNumber);
57       * </pre>
58       */
59      public static final long NONE = 0;
60  
61      /**
62       * Option specifying that American Express cards are allowed.
63       */
64      public static final long AMEX = 1 << 0;
65  
66      /**
67       * Option specifying that Visa cards are allowed.
68       */
69      public static final long VISA = 1 << 1;
70  
71      /**
72       * Option specifying that Mastercard cards are allowed.
73       */
74      public static final long MASTERCARD = 1 << 2;
75  
76      /**
77       * Option specifying that Discover cards are allowed.
78       */
79      public static final long DISCOVER = 1 << 3;
80  
81      /**
82       * Option specifying that Diners cards are allowed.
83       */
84      public static final long DINERS = 1 << 4;
85  
86      /**
87       * The CreditCardTypes that are allowed to pass validation.
88       */
89      private final List cardTypes = new ArrayList();
90  
91      /**
92       * Luhn checkdigit validator for the card numbers.
93       */
94      private static final CheckDigit LUHN_VALIDATOR = LuhnCheckDigit.LUHN_CHECK_DIGIT;
95  
96      /** American Express (Amex) Card Validator */
97      public static final CodeValidator AMEX_VALIDATOR = new CodeValidator("^(3[47]\\d{13})$", LUHN_VALIDATOR);
98  
99      /** Diners Card Validator */
100     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);
101 
102     /** Discover Card regular expressions */
103     private static final RegexValidator DISCOVER_REGEX = new RegexValidator(new String[] {"^(6011\\d{12})$", "^(64[4-9]\\d{13})$", "^(65\\d{14})$"});
104 
105     /** Discover Card Validator */
106     public static final CodeValidator DISCOVER_VALIDATOR = new CodeValidator(DISCOVER_REGEX, LUHN_VALIDATOR);
107 
108     /** Mastercard Card Validator */
109     public static final CodeValidator MASTERCARD_VALIDATOR = new CodeValidator("^(5[1-5]\\d{14})$", LUHN_VALIDATOR);
110 
111     /** Visa Card Validator */
112     public static final CodeValidator VISA_VALIDATOR = new CodeValidator("^(4)(\\d{12}|\\d{15})$", LUHN_VALIDATOR);
113 
114     /**
115      * Create a new CreditCardValidator with default options.
116      */
117     public CreditCardValidator() {
118         this(AMEX + VISA + MASTERCARD + DISCOVER);
119     }
120 
121     /**
122      * Create a new CreditCardValidator with the specified options.
123      * @param options Pass in
124      * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
125      * those are the only valid card types.
126      */
127     public CreditCardValidator(long options) {
128         super();
129 
130         if (isOn(options, VISA)) {
131             this.cardTypes.add(VISA_VALIDATOR);
132         }
133 
134         if (isOn(options, AMEX)) {
135             this.cardTypes.add(AMEX_VALIDATOR);
136         }
137 
138         if (isOn(options, MASTERCARD)) {
139             this.cardTypes.add(MASTERCARD_VALIDATOR);
140         }
141 
142         if (isOn(options, DISCOVER)) {
143             this.cardTypes.add(DISCOVER_VALIDATOR);
144         }
145 
146         if (isOn(options, DINERS)) {
147             this.cardTypes.add(DINERS_VALIDATOR);
148         }
149     }
150 
151     /**
152      * Create a new CreditCardValidator with the specified {@link CodeValidator}s.
153      * @param creditCardValidators Set of valid code validators
154      */
155     public CreditCardValidator(CodeValidator[] creditCardValidators) {
156         if (creditCardValidators == null) {
157             throw new IllegalArgumentException("Card validators are missing");
158         }
159         for (int i = 0; i < creditCardValidators.length; i++) {
160             cardTypes.add(creditCardValidators[i]);
161         }
162     }
163 
164     /**
165      * Checks if the field is a valid credit card number.
166      * @param card The card number to validate.
167      * @return Whether the card number is valid.
168      */
169     public boolean isValid(String card) {
170         if (card == null || card.length() == 0) {
171             return false;
172         }
173         for (int i = 0; i < cardTypes.size(); i++) {
174             CodeValidator type = (CodeValidator)cardTypes.get(i);
175             if (type.isValid(card)) {
176                 return true;
177             }
178         }
179         return false;
180     }
181 
182     /**
183      * Checks if the field is a valid credit card number.
184      * @param card The card number to validate.
185      * @return The card number if valid or <code>null</code>
186      * if invalid.
187      */
188     public Object validate(String card) {
189         if (card == null || card.length() == 0) {
190             return null;
191         }
192         Object result = null;
193         for (int i = 0; i < cardTypes.size(); i++) {
194             CodeValidator type = (CodeValidator)cardTypes.get(i);
195             result = type.validate(card);
196             if (result != null) {
197                 return result ;
198             }
199         }
200         return null;
201 
202     }
203     /**
204      * Tests whether the given flag is on.  If the flag is not a power of 2
205      * (ie. 3) this tests whether the combination of flags is on.
206      *
207      * @param options The options specified.
208      * @param flag Flag value to check.
209      *
210      * @return whether the specified flag value is on.
211      */
212     private boolean isOn(long options, long flag) {
213         return (options & flag) > 0;
214     }
215 
216 }