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 */
017package org.apache.commons.validator.routines;
018
019import org.apache.commons.validator.routines.checkdigit.CheckDigit;
020import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit;
021import java.io.Serializable;
022import java.util.Collections;
023import java.util.List;
024import java.util.ArrayList;
025
026/**
027 * Perform credit card validations.
028 *
029 * <p>
030 * By default, all supported card types are allowed.  You can specify which
031 * cards should pass validation by configuring the validation options. For
032 * example,
033 * </p>
034 *
035 * <pre>
036 * <code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
037 * </pre>
038 *
039 * <p>
040 * configures the validator to only pass American Express and Visa cards.
041 * If a card type is not directly supported by this class, you can implement
042 * the CreditCardType interface and pass an instance into the
043 * <code>addAllowedCardType</code> method.
044 * </p>
045 *
046 * <p>
047 * For a similar implementation in Perl, reference Sean M. Burke's
048 * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
049 * More information can be found in Michael Gilleland's essay 
050 * <a href="http://web.archive.org/web/20120614072656/http://www.merriampark.com/anatomycc.htm">Anatomy of Credit Card Numbers</a>.
051 * </p>
052 *
053 * @version $Revision: 1739207 $
054 * @since Validator 1.4
055 */
056public class CreditCardValidator implements Serializable {
057
058    private static final long serialVersionUID = 5955978921148959496L;
059
060    /**
061     * Option specifying that no cards are allowed.  This is useful if
062     * you want only custom card types to validate so you turn off the
063     * default cards with this option.
064     *
065     * <pre>
066     * <code>
067     * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
068     * v.addAllowedCardType(customType);
069     * v.isValid(aCardNumber);
070     * </code>
071     * </pre>
072     */
073    public static final long NONE = 0;
074
075    /**
076     * Option specifying that American Express cards are allowed.
077     */
078    public static final long AMEX = 1 << 0;
079
080    /**
081     * Option specifying that Visa cards are allowed.
082     */
083    public static final long VISA = 1 << 1;
084
085    /**
086     * Option specifying that Mastercard cards are allowed.
087     */
088    public static final long MASTERCARD = 1 << 2;
089
090    /**
091     * Option specifying that Discover cards are allowed.
092     */
093    public static final long DISCOVER = 1 << 3; // CHECKSTYLE IGNORE MagicNumber
094
095    /**
096     * Option specifying that Diners cards are allowed.
097     */
098    public static final long DINERS = 1 << 4; // CHECKSTYLE IGNORE MagicNumber
099
100    /**
101     * Option specifying that VPay (Visa) cards are allowed.
102     * @since 1.5.0
103     */
104    public static final long VPAY = 1 << 5; // CHECKSTYLE IGNORE MagicNumber
105
106    /**
107     * Option specifying that Mastercard cards (pre Oct 2016 only) are allowed.
108     * @deprecated for use until Oct 2016 only
109     */
110    @Deprecated
111    public static final long MASTERCARD_PRE_OCT2016 = 1 << 6; // CHECKSTYLE IGNORE MagicNumber
112
113
114    /**
115     * The CreditCardTypes that are allowed to pass validation.
116     */
117    private final List<CodeValidator> cardTypes = new ArrayList<CodeValidator>();
118
119    /**
120     * Luhn checkdigit validator for the card numbers.
121     */
122    private static final CheckDigit LUHN_VALIDATOR = LuhnCheckDigit.LUHN_CHECK_DIGIT;
123
124    /** American Express (Amex) Card Validator */
125    public static final CodeValidator AMEX_VALIDATOR = new CodeValidator("^(3[47]\\d{13})$", LUHN_VALIDATOR);
126
127    /** Diners Card Validator */
128    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);
129
130    /** Discover Card regular expressions */
131    private static final RegexValidator DISCOVER_REGEX = new RegexValidator(new String[] {"^(6011\\d{12})$", "^(64[4-9]\\d{13})$", "^(65\\d{14})$"});
132
133    /** Discover Card Validator */
134    public static final CodeValidator DISCOVER_VALIDATOR = new CodeValidator(DISCOVER_REGEX, LUHN_VALIDATOR);
135
136    /** Mastercard regular expressions */
137    private static final RegexValidator MASTERCARD_REGEX = new RegexValidator(
138        new String[] {
139            "^(5[1-5]\\d{14})$",  // 51 - 55 (pre Oct 2016)
140            // valid from October 2016
141            "^(2221\\d{12})$",    // 222100 - 222199
142            "^(222[2-9]\\d{12})$",// 222200 - 222999
143            "^(22[3-9]\\d{13})$", // 223000 - 229999
144            "^(2[3-6]\\d{14})$",  // 230000 - 269999
145            "^(27[01]\\d{13})$",  // 270000 - 271999
146            "^(2720\\d{12})$",    // 272000 - 272099
147        });
148
149    /** Mastercard Card Validator */
150    public static final CodeValidator MASTERCARD_VALIDATOR = new CodeValidator(MASTERCARD_REGEX, LUHN_VALIDATOR);
151
152    /** 
153     * Mastercard Card Validator (pre Oct 2016)
154     * @deprecated for use until Oct 2016 only
155     */
156    @Deprecated
157    public static final CodeValidator MASTERCARD_VALIDATOR_PRE_OCT2016 = new CodeValidator("^(5[1-5]\\d{14})$", LUHN_VALIDATOR);
158
159    /** Visa Card Validator */
160    public static final CodeValidator VISA_VALIDATOR = new CodeValidator("^(4)(\\d{12}|\\d{15})$", LUHN_VALIDATOR);
161
162    /** VPay (Visa) Card Validator 
163     * @since 1.5.0
164     */
165    public static final CodeValidator VPAY_VALIDATOR = new CodeValidator("^(4)(\\d{12,18})$", LUHN_VALIDATOR);
166
167    /**
168     * Create a new CreditCardValidator with default options.
169     * The default options are:
170     * AMEX, VISA, MASTERCARD and DISCOVER
171     */
172    public CreditCardValidator() {
173        this(AMEX + VISA + MASTERCARD + DISCOVER);
174    }
175
176    /**
177     * Create a new CreditCardValidator with the specified options.
178     * @param options Pass in
179     * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
180     * those are the only valid card types.
181     */
182    public CreditCardValidator(long options) {
183        super();
184
185        if (isOn(options, VISA)) {
186            this.cardTypes.add(VISA_VALIDATOR);
187        }
188
189        if (isOn(options, VPAY)) {
190            this.cardTypes.add(VPAY_VALIDATOR);
191        }
192
193        if (isOn(options, AMEX)) {
194            this.cardTypes.add(AMEX_VALIDATOR);
195        }
196
197        if (isOn(options, MASTERCARD)) {
198            this.cardTypes.add(MASTERCARD_VALIDATOR);
199        }
200
201        if (isOn(options, MASTERCARD_PRE_OCT2016)) {
202            this.cardTypes.add(MASTERCARD_VALIDATOR_PRE_OCT2016);
203        }
204
205        if (isOn(options, DISCOVER)) {
206            this.cardTypes.add(DISCOVER_VALIDATOR);
207        }
208
209        if (isOn(options, DINERS)) {
210            this.cardTypes.add(DINERS_VALIDATOR);
211        }
212    }
213
214    /**
215     * Create a new CreditCardValidator with the specified {@link CodeValidator}s.
216     * @param creditCardValidators Set of valid code validators
217     */
218    public CreditCardValidator(CodeValidator[] creditCardValidators) {
219        if (creditCardValidators == null) {
220            throw new IllegalArgumentException("Card validators are missing");
221        }
222        Collections.addAll(cardTypes, creditCardValidators);
223    }
224
225    /**
226     * Checks if the field is a valid credit card number.
227     * @param card The card number to validate.
228     * @return Whether the card number is valid.
229     */
230    public boolean isValid(String card) {
231        if (card == null || card.length() == 0) {
232            return false;
233        }
234        for (CodeValidator cardType : cardTypes) {
235            if (cardType.isValid(card)) {
236                return true;
237            }
238        }
239        return false;
240    }
241
242    /**
243     * Checks if the field is a valid credit card number.
244     * @param card The card number to validate.
245     * @return The card number if valid or <code>null</code>
246     * if invalid.
247     */
248    public Object validate(String card) {
249        if (card == null || card.length() == 0) {
250            return null;
251        }
252        Object result = null;
253        for (CodeValidator cardType : cardTypes) {
254            result = cardType.validate(card);
255            if (result != null) {
256                return result;
257            }
258        }
259        return null;
260
261    }
262    /**
263     * Tests whether the given flag is on.  If the flag is not a power of 2
264     * (ie. 3) this tests whether the combination of flags is on.
265     *
266     * @param options The options specified.
267     * @param flag Flag value to check.
268     *
269     * @return whether the specified flag value is on.
270     */
271    private boolean isOn(long options, long flag) {
272        return (options & flag) > 0;
273    }
274
275}