001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.validator.routines;
018
019 import org.apache.commons.validator.routines.checkdigit.CheckDigit;
020 import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit;
021 import java.io.Serializable;
022 import java.util.List;
023 import java.util.ArrayList;
024
025 /**
026 * <p>Perform credit card validations.</p>
027 * <p>
028 * By default, all supported card types are allowed. You can specify which
029 * cards should pass validation by configuring the validation options. For
030 * example,<br/><code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
031 * configures the validator to only pass American Express and Visa cards.
032 * If a card type is not directly supported by this class, you can implement
033 * the CreditCardType interface and pass an instance into the
034 * <code>addAllowedCardType</code> method.
035 * </p>
036 * For a similar implementation in Perl, reference Sean M. Burke's
037 * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
038 * More information is also available
039 * <a href="http://www.merriampark.com/anatomycc.htm">here</a>.
040 *
041 * @version $Revision: 594937 $ $Date: 2007-11-14 17:34:43 +0100 (Mi, 14. Nov 2007) $
042 * @since Validator 1.4
043 */
044 public class CreditCardValidator implements Serializable {
045
046 /**
047 * Option specifying that no cards are allowed. This is useful if
048 * you want only custom card types to validate so you turn off the
049 * default cards with this option.
050 * <br/>
051 * <pre>
052 * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
053 * v.addAllowedCardType(customType);
054 * v.isValid(aCardNumber);
055 * </pre>
056 */
057 public static final long NONE = 0;
058
059 /**
060 * Option specifying that American Express cards are allowed.
061 */
062 public static final long AMEX = 1 << 0;
063
064 /**
065 * Option specifying that Visa cards are allowed.
066 */
067 public static final long VISA = 1 << 1;
068
069 /**
070 * Option specifying that Mastercard cards are allowed.
071 */
072 public static final long MASTERCARD = 1 << 2;
073
074 /**
075 * Option specifying that Discover cards are allowed.
076 */
077 public static final long DISCOVER = 1 << 3;
078
079 /**
080 * Option specifying that Diners cards are allowed.
081 */
082 public static final long DINERS = 1 << 4;
083
084 /**
085 * The CreditCardTypes that are allowed to pass validation.
086 */
087 private final List cardTypes = new ArrayList();
088
089 /**
090 * Luhn checkdigit validator for the card numbers.
091 */
092 private static final CheckDigit LUHN_VALIDATOR = LuhnCheckDigit.INSTANCE;
093
094 /** American Express (Amex) Card Validator */
095 public static final CodeValidator AMEX_VALIDATOR = new CodeValidator("^(3[47]\\d{13})$", LUHN_VALIDATOR);
096
097 /** Diners Card Validator */
098 public static final CodeValidator DINERS_VALIDATOR = new CodeValidator("^(30[0-5]\\d{11}|36\\d{12})$", LUHN_VALIDATOR);
099
100 /** Discover Card regular expressions */
101 private static final RegexValidator DISCOVER_REGEX = new RegexValidator(new String[] {"^(6011\\d{12})$", "^(65\\d{14})$"});
102
103 /** Discover Card Validator */
104 public static final CodeValidator DISCOVER_VALIDATOR = new CodeValidator(DISCOVER_REGEX, LUHN_VALIDATOR);
105
106 /** Mastercard Card Validator */
107 public static final CodeValidator MASTERCARD_VALIDATOR = new CodeValidator("^(5[1-5]\\d{14})$", LUHN_VALIDATOR);
108
109 /** Visa Card Validator */
110 public static final CodeValidator VISA_VALIDATOR = new CodeValidator("^(4)(\\d{12}|\\d{15})$", LUHN_VALIDATOR);
111
112 /**
113 * Create a new CreditCardValidator with default options.
114 */
115 public CreditCardValidator() {
116 this(AMEX + VISA + MASTERCARD + DISCOVER);
117 }
118
119 /**
120 * Create a new CreditCardValidator with the specified options.
121 * @param options Pass in
122 * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
123 * those are the only valid card types.
124 */
125 public CreditCardValidator(long options) {
126 super();
127
128 if (isOn(options, VISA)) {
129 this.cardTypes.add(VISA_VALIDATOR);
130 }
131
132 if (isOn(options, AMEX)) {
133 this.cardTypes.add(AMEX_VALIDATOR);
134 }
135
136 if (isOn(options, MASTERCARD)) {
137 this.cardTypes.add(MASTERCARD_VALIDATOR);
138 }
139
140 if (isOn(options, DISCOVER)) {
141 this.cardTypes.add(DISCOVER_VALIDATOR);
142 }
143
144 if (isOn(options, DINERS)) {
145 this.cardTypes.add(DINERS_VALIDATOR);
146 }
147 }
148
149 /**
150 * Create a new CreditCardValidator with the specified {@link CodeValidator}s.
151 * @param creditCardValidators Set of valid code validators
152 */
153 public CreditCardValidator(CodeValidator[] creditCardValidators) {
154 if (creditCardValidators == null) {
155 throw new IllegalArgumentException("Card validators are missing");
156 }
157 for (int i = 0; i < creditCardValidators.length; i++) {
158 cardTypes.add(creditCardValidators[i]);
159 }
160 }
161
162 /**
163 * Checks if the field is a valid credit card number.
164 * @param card The card number to validate.
165 * @return Whether the card number is valid.
166 */
167 public boolean isValid(String card) {
168 if (card == null || card.length() == 0) {
169 return false;
170 }
171 for (int i = 0; i < cardTypes.size(); i++) {
172 CodeValidator type = (CodeValidator)cardTypes.get(i);
173 if (type.isValid(card)) {
174 return true;
175 }
176 }
177 return false;
178 }
179
180 /**
181 * Checks if the field is a valid credit card number.
182 * @param card The card number to validate.
183 * @return The card number if valid or <code>null</code>
184 * if invalid.
185 */
186 public Object validate(String card) {
187 if (card == null || card.length() == 0) {
188 return null;
189 }
190 Object result = null;
191 for (int i = 0; i < cardTypes.size(); i++) {
192 CodeValidator type = (CodeValidator)cardTypes.get(i);
193 result = type.validate(card);
194 if (result != null) {
195 return result ;
196 }
197 }
198 return null;
199
200 }
201 /**
202 * Tests whether the given flag is on. If the flag is not a power of 2
203 * (ie. 3) this tests whether the combination of flags is on.
204 *
205 * @param options The options specified.
206 * @param flag Flag value to check.
207 *
208 * @return whether the specified flag value is on.
209 */
210 private boolean isOn(long options, long flag) {
211 return (options & flag) > 0;
212 }
213
214 }