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 12:45:51 -0500 (Thu, 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 }