1 /*
2 * $Header: /home/cvs/jakarta-commons/validator/src/share/org/apache/commons/validator/CreditCardValidator.java,v 1.16 2004/02/21 17:10:29 rleland Exp $
3 * $Revision: 1.16 $
4 * $Date: 2004/02/21 17:10:29 $
5 *
6 * ====================================================================
7 * Copyright 2001-2004 The Apache Software Foundation
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 package org.apache.commons.validator;
23
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Iterator;
27
28 import org.apache.commons.validator.util.Flags;
29
30 /***
31 * <p>Perform credit card validations.</p>
32 * <p>
33 * By default, all supported card types are allowed. You can specify which
34 * cards should pass validation by configuring the validation options. For
35 * example,<br/><code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
36 * configures the validator to only pass American Express and Visa cards.
37 * If a card type is not directly supported by this class, you can implement
38 * the CreditCardType interface and pass an instance into the
39 * <code>addAllowedCardType</code> method.
40 * </p>
41 * For a similar implementation in Perl, reference Sean M. Burke's
42 * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
43 * More information is also available
44 * <a href="http://www.merriampark.com/anatomycc.htm">here</a>.
45 *
46 * @since Validator 1.1
47 */
48 public class CreditCardValidator {
49
50 /***
51 * Option specifying that no cards are allowed. This is useful if
52 * you want only custom card types to validate so you turn off the
53 * default cards with this option.
54 * <br/>
55 * <pre>
56 * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
57 * v.addAllowedCardType(customType);
58 * v.isValid(aCardNumber);
59 * </pre>
60 * @since Validator 1.1.2
61 */
62 public static final int NONE = 0;
63
64 /***
65 * Option specifying that American Express cards are allowed.
66 */
67 public static final int AMEX = 1 << 0;
68
69 /***
70 * Option specifying that Visa cards are allowed.
71 */
72 public static final int VISA = 1 << 1;
73
74 /***
75 * Option specifying that Mastercard cards are allowed.
76 */
77 public static final int MASTERCARD = 1 << 2;
78
79 /***
80 * Option specifying that Discover cards are allowed.
81 */
82 public static final int DISCOVER = 1 << 3;
83
84 /***
85 * The CreditCardTypes that are allowed to pass validation.
86 */
87 private Collection cardTypes = new ArrayList();
88
89 /***
90 * Create a new CreditCardValidator with default options.
91 */
92 public CreditCardValidator() {
93 this(AMEX + VISA + MASTERCARD + DISCOVER);
94 }
95
96 /***
97 * Create a new CreditCardValidator with the specified options.
98 * @param options Pass in
99 * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
100 * those are the only valid card types.
101 */
102 public CreditCardValidator(int options) {
103 super();
104
105 Flags f = new Flags(options);
106 if (f.isOn(VISA)) {
107 this.cardTypes.add(new Visa());
108 }
109
110 if (f.isOn(AMEX)) {
111 this.cardTypes.add(new Amex());
112 }
113
114 if (f.isOn(MASTERCARD)) {
115 this.cardTypes.add(new Mastercard());
116 }
117
118 if (f.isOn(DISCOVER)) {
119 this.cardTypes.add(new Discover());
120 }
121 }
122
123 /***
124 * Checks if the field is a valid credit card number.
125 * @param card The card number to validate.
126 */
127 public boolean isValid(String card) {
128 if ((card == null) || (card.length() < 13) || (card.length() > 19)) {
129 return false;
130 }
131
132 if (!this.luhnCheck(card)) {
133 return false;
134 }
135
136 Iterator types = this.cardTypes.iterator();
137 while (types.hasNext()) {
138 CreditCardType type = (CreditCardType) types.next();
139 if (type.matches(card)) {
140 return true;
141 }
142 }
143
144 return false;
145 }
146
147 /***
148 * Add an allowed CreditCardType that participates in the card
149 * validation algorithm.
150 * @param type The type that is now allowed to pass validation.
151 * @since Validator 1.1.2
152 */
153 public void addAllowedCardType(CreditCardType type){
154 this.cardTypes.add(type);
155 }
156
157 /***
158 * Checks for a valid credit card number.
159 * @param cardNumber Credit Card Number.
160 */
161 protected boolean luhnCheck(String cardNumber) {
162 // number must be validated as 0..9 numeric first!!
163 int digits = cardNumber.length();
164 int oddOrEven = digits & 1;
165 long sum = 0;
166 for (int count = 0; count < digits; count++) {
167 int digit = 0;
168 try {
169 digit = Integer.parseInt(cardNumber.charAt(count) + "");
170 } catch(NumberFormatException e) {
171 return false;
172 }
173
174 if (((count & 1) ^ oddOrEven) == 0) { // not
175 digit *= 2;
176 if (digit > 9) {
177 digit -= 9;
178 }
179 }
180 sum += digit;
181 }
182
183 return (sum == 0) ? false : (sum % 10 == 0);
184 }
185
186 /***
187 * Checks for a valid credit card number.
188 * @param card Credit Card Number.
189 * @deprecated This will be removed in a future release.
190 */
191 protected boolean isValidPrefix(String card) {
192
193 if (card.length() < 13) {
194 return false;
195 }
196
197 return new Visa().matches(card)
198 || new Amex().matches(card)
199 || new Mastercard().matches(card)
200 || new Discover().matches(card);
201 }
202
203 /***
204 * CreditCardType implementations define how validation is performed
205 * for one type/brand of credit card.
206 * @since Validator 1.1.2
207 */
208 public interface CreditCardType {
209
210 /***
211 * Returns true if the card number matches this type of credit
212 * card. Note that this method is <strong>not</strong> responsible
213 * for analyzing the general form of the card number because
214 * <code>CreditCardValidator</code> performs those checks before
215 * calling this method. It is generally only required to valid the
216 * length and prefix of the number to determine if it's the correct
217 * type.
218 * @param card The card number, never null.
219 * @return true if the number matches.
220 */
221 boolean matches(String card);
222
223 }
224
225 private class Visa implements CreditCardType {
226 private static final String PREFIX = "4";
227 public boolean matches(String card) {
228 return (
229 card.substring(0, 1).equals(PREFIX)
230 && (card.length() == 13 || card.length() == 16));
231 }
232 }
233
234 private class Amex implements CreditCardType {
235 private static final String PREFIX = "34,37,";
236 public boolean matches(String card) {
237 String prefix2 = card.substring(0, 2) + ",";
238 return ((PREFIX.indexOf(prefix2) != -1) && (card.length() == 15));
239 }
240 }
241
242 private class Discover implements CreditCardType {
243 private static final String PREFIX = "6011";
244 public boolean matches(String card) {
245 return (card.substring(0, 4).equals(PREFIX) && (card.length() == 16));
246 }
247 }
248
249 private class Mastercard implements CreditCardType {
250 private static final String PREFIX = "51,52,53,54,55,";
251 public boolean matches(String card) {
252 String prefix2 = card.substring(0, 2) + ",";
253 return ((PREFIX.indexOf(prefix2) != -1) && (card.length() == 16));
254 }
255 }
256
257 }
This page was automatically generated by Maven