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;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  
22  import org.apache.commons.validator.util.Flags;
23  
24  /**
25   * Perform credit card validations.
26   *
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,
31   * </p>
32   *
33   * <pre>
34   * <code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
35   * </pre>
36   *
37   * <p>
38   * configures the validator to only pass American Express and Visa cards.
39   * If a card type is not directly supported by this class, you can implement
40   * the CreditCardType interface and pass an instance into the
41   * <code>addAllowedCardType</code> method.
42   * </p>
43   *
44   * <p>
45   * For a similar implementation in Perl, reference Sean M. Burke's
46   * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
47   * More information is also available
48   * <a href="http://www.merriampark.com/anatomycc.htm">here</a>.
49   * </p>
50   *
51   * @since 1.1
52   * @deprecated Use the new CreditCardValidator in the routines package. This class
53   * will be removed in a future release.
54   */
55  // CHECKSTYLE:OFF (deprecated code)
56  @Deprecated
57  public class CreditCardValidator {
58  
59      private static class Amex implements CreditCardType {
60          static final Amex INSTANCE = new Amex();
61          private static final String PREFIX = "34,37,";
62          @Override
63          public boolean matches(final String card) {
64              final String prefix2 = card.substring(0, 2) + ",";
65              return PREFIX.contains(prefix2) && card.length() == 15;
66          }
67      }
68  
69      /**
70       * CreditCardType implementations define how validation is performed
71       * for one type/brand of credit card.
72       * @since 1.1.2
73       */
74      public interface CreditCardType {
75  
76          /**
77           * Returns true if the card number matches this type of credit
78           * card.  Note that this method is <strong>not</strong> responsible
79           * for analyzing the general form of the card number because
80           * <code>CreditCardValidator</code> performs those checks before
81           * calling this method.  It is generally only required to valid the
82           * length and prefix of the number to determine if it's the correct
83           * type.
84           * @param card The card number, never null.
85           * @return true if the number matches.
86           */
87          boolean matches(String card);
88  
89      }
90  
91      private static class Discover implements CreditCardType {
92          static final Discover INSTANCE = new Discover();
93          private static final String PREFIX = "6011";
94          @Override
95          public boolean matches(final String card) {
96              return card.substring(0, 4).equals(PREFIX) && card.length() == 16;
97          }
98      }
99  
100     private static class Mastercard implements CreditCardType {
101         static final Mastercard INSTANCE = new Mastercard();
102         private static final String PREFIX = "51,52,53,54,55,";
103         @Override
104         public boolean matches(final String card) {
105             final String prefix2 = card.substring(0, 2) + ",";
106             return PREFIX.contains(prefix2) && card.length() == 16;
107         }
108     }
109 
110     /**
111      *  Change to support Visa Carte Blue used in France
112      *  has been removed - see Bug 35926
113      */
114     private static class Visa implements CreditCardType {
115         static final Visa INSTANCE = new Visa();
116         private static final String PREFIX = "4";
117 
118         @Override
119         public boolean matches(final String card) {
120             return card.substring(0, 1).equals(PREFIX) && (card.length() == 13 || card.length() == 16);
121         }
122     }
123 
124     /**
125      * Option specifying that no cards are allowed.  This is useful if
126      * you want only custom card types to validate so you turn off the
127      * default cards with this option.
128      * <pre>
129      * <code>
130      * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
131      * v.addAllowedCardType(customType);
132      * v.isValid(aCardNumber);
133      * </code>
134      * </pre>
135      * @since 1.1.2
136      */
137     public static final int NONE = 0;
138 
139     /**
140      * Option specifying that American Express cards are allowed.
141      */
142     public static final int AMEX = 1 << 0;
143 
144     /**
145      * Option specifying that Visa cards are allowed.
146      */
147     public static final int VISA = 1 << 1;
148 
149     /**
150      * Option specifying that Mastercard cards are allowed.
151      */
152     public static final int MASTERCARD = 1 << 2;
153 
154     /**
155      * Option specifying that Discover cards are allowed.
156      */
157     public static final int DISCOVER = 1 << 3;
158 
159     /**
160      * The CreditCardTypes that are allowed to pass validation.
161      */
162     private final Collection<CreditCardType> cardTypes = new ArrayList<>();
163 
164     /**
165      * Create a new CreditCardValidator with default options.
166      */
167     public CreditCardValidator() {
168         this(AMEX + VISA + MASTERCARD + DISCOVER);
169     }
170 
171     /**
172      * Creates a new CreditCardValidator with the specified options.
173      * @param options Pass in
174      * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
175      * those are the only valid card types.
176      */
177     public CreditCardValidator(final int options) {
178         final Flags f = new Flags(options);
179         if (f.isOn(VISA)) {
180             this.cardTypes.add(Visa.INSTANCE);
181         }
182 
183         if (f.isOn(AMEX)) {
184             this.cardTypes.add(Amex.INSTANCE);
185         }
186 
187         if (f.isOn(MASTERCARD)) {
188             this.cardTypes.add(Mastercard.INSTANCE);
189         }
190 
191         if (f.isOn(DISCOVER)) {
192             this.cardTypes.add(Discover.INSTANCE);
193         }
194     }
195 
196     /**
197      * Adds an allowed CreditCardType that participates in the card
198      * validation algorithm.
199      * @param type The type that is now allowed to pass validation.
200      * @since 1.1.2
201      */
202     public void addAllowedCardType(final CreditCardType type){
203         this.cardTypes.add(type);
204     }
205 
206     /**
207      * Checks if the field is a valid credit card number.
208      * @param card The card number to validate.
209      * @return Whether the card number is valid.
210      */
211     public boolean isValid(final String card) {
212         if (card == null || card.length() < 13 || card.length() > 19) {
213             return false;
214         }
215 
216         if (!this.luhnCheck(card)) {
217             return false;
218         }
219 
220         for (final Object cardType : this.cardTypes) {
221             final CreditCardType type = (CreditCardType) cardType;
222             if (type.matches(card)) {
223                 return true;
224             }
225         }
226 
227         return false;
228     }
229 
230     /**
231      * Checks for a valid credit card number.
232      * @param cardNumber Credit Card Number.
233      * @return Whether the card number passes the luhnCheck.
234      */
235     protected boolean luhnCheck(final String cardNumber) {
236         // number must be validated as 0..9 numeric first!!
237         final int digits = cardNumber.length();
238         final int oddOrEven = digits & 1;
239         long sum = 0;
240         for (int count = 0; count < digits; count++) {
241             int digit = 0;
242             try {
243                 digit = Integer.parseInt(cardNumber.charAt(count) + "");
244             } catch (final NumberFormatException e) {
245                 return false;
246             }
247 
248             if ((count & 1 ^ oddOrEven) == 0) { // not
249                 digit *= 2;
250                 if (digit > 9) {
251                     digit -= 9;
252                 }
253             }
254             sum += digit;
255         }
256 
257         return sum == 0 ? false : sum % 10 == 0;
258     }
259 
260 }