    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;
    018
    019import java.util.ArrayList;
    020import java.util.Collection;
    021
    022import org.apache.commons.validator.util.Flags;
    023
    024/**
    025 * Perform credit card validations.
    026 *
    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,
    031 * </p>
    032 *
    033 * <pre>
    034 * <code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
    035 * </pre>
    036 *
    037 * <p>
    038 * configures the validator to only pass American Express and Visa cards.
    039 * If a card type is not directly supported by this class, you can implement
    040 * the CreditCardType interface and pass an instance into the
    041 * <code>addAllowedCardType</code> method.
    042 * </p>
    043 *
    044 * <p>
    045 * For a similar implementation in Perl, reference Sean M. Burke's
    046 * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
    047 * More information is also available
    048 * <a href="http://www.merriampark.com/anatomycc.htm">here</a>.
    049 * </p>
    050 *
    051 * @version $Revision$
    052 * @since Validator 1.1
    053 * @deprecated Use the new CreditCardValidator in the routines package. This class
    054 * will be removed in a future release.
    055 */
    056// CHECKSTYLE:OFF (deprecated code)
    057@Deprecated
    058public class CreditCardValidator {
    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     * <pre>
    065     * <code>
    066     * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
    067     * v.addAllowedCardType(customType);
    068     * v.isValid(aCardNumber);
    069     * </code>
    070     * </pre>
    071     * @since Validator 1.1.2
    072     */
    073    public static final int NONE = 0;
    074
    075    /**
    076     * Option specifying that American Express cards are allowed.
    077     */
    078    public static final int AMEX = 1 << 0;
    079
    080    /**
    081     * Option specifying that Visa cards are allowed.
    082     */
    083    public static final int VISA = 1 << 1;
    084
    085    /**
    086     * Option specifying that Mastercard cards are allowed.
    087     */
    088    public static final int MASTERCARD = 1 << 2;
    089
    090    /**
    091     * Option specifying that Discover cards are allowed.
    092     */
    093    public static final int DISCOVER = 1 << 3;
    094
    095    /**
    096     * The CreditCardTypes that are allowed to pass validation.
    097     */
    098    private final Collection<CreditCardType> cardTypes = new ArrayList<CreditCardType>();
    099
    100    /**
    101     * Create a new CreditCardValidator with default options.
    102     */
    103    public CreditCardValidator() {
    104        this(AMEX + VISA + MASTERCARD + DISCOVER);
    105    }
    106
    107    /**
    108     * Creates a new CreditCardValidator with the specified options.
    109     * @param options Pass in
    110     * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
    111     * those are the only valid card types.
    112     */
    113    public CreditCardValidator(int options) {
    114        super();
    115
    116        Flags f = new Flags(options);
    117        if (f.isOn(VISA)) {
    118            this.cardTypes.add(new Visa());
    119        }
    120
    121        if (f.isOn(AMEX)) {
    122            this.cardTypes.add(new Amex());
    123        }
    124
    125        if (f.isOn(MASTERCARD)) {
    126            this.cardTypes.add(new Mastercard());
    127        }
    128
    129        if (f.isOn(DISCOVER)) {
    130            this.cardTypes.add(new Discover());
    131        }
    132    }
    133
    134    /**
    135     * Checks if the field is a valid credit card number.
    136     * @param card The card number to validate.
    137     * @return Whether the card number is valid.
    138     */
    139    public boolean isValid(String card) {
    140        if ((card == null) || (card.length() < 13) || (card.length() > 19)) {
    141            return false;
    142        }
    143
    144        if (!this.luhnCheck(card)) {
    145            return false;
    146        }
    147
    148        for (Object cardType : this.cardTypes) {
    149            CreditCardType type = (CreditCardType) cardType;
    150            if (type.matches(card)) {
    151                return true;
    152            }
    153        }
    154
    155        return false;
    156    }
    157
    158    /**
    159     * Adds an allowed CreditCardType that participates in the card
    160     * validation algorithm.
    161     * @param type The type that is now allowed to pass validation.
    162     * @since Validator 1.1.2
    163     */
    164    public void addAllowedCardType(CreditCardType type){
    165        this.cardTypes.add(type);
    166    }
    167
    168    /**
    169     * Checks for a valid credit card number.
    170     * @param cardNumber Credit Card Number.
    171     * @return Whether the card number passes the luhnCheck.
    172     */
    173    protected boolean luhnCheck(String cardNumber) {
    174        // number must be validated as 0..9 numeric first!!
    175        int digits = cardNumber.length();
    176        int oddOrEven = digits & 1;
    177        long sum = 0;
    178        for (int count = 0; count < digits; count++) {
    179            int digit = 0;
    180            try {
    181                digit = Integer.parseInt(cardNumber.charAt(count) + "");
    182            } catch(NumberFormatException e) {
    183                return false;
    184            }
    185
    186            if (((count & 1) ^ oddOrEven) == 0) { // not
    187                digit *= 2;
    188                if (digit > 9) {
    189                    digit -= 9;
    190                }
    191            }
    192            sum += digit;
    193        }
    194
    195        return (sum == 0) ? false : (sum % 10 == 0);
    196    }
    197
    198    /**
    199     * CreditCardType implementations define how validation is performed
    200     * for one type/brand of credit card.
    201     * @since Validator 1.1.2
    202     */
    203    public interface CreditCardType {
    204
    205        /**
    206         * Returns true if the card number matches this type of credit
    207         * card.  Note that this method is <strong>not</strong> responsible
    208         * for analyzing the general form of the card number because
    209         * <code>CreditCardValidator</code> performs those checks before
    210         * calling this method.  It is generally only required to valid the
    211         * length and prefix of the number to determine if it's the correct
    212         * type.
    213         * @param card The card number, never null.
    214         * @return true if the number matches.
    215         */
    216        boolean matches(String card);
    217
    218    }
    219
    220    /**
    221     *  Change to support Visa Carte Blue used in France
    222     *  has been removed - see Bug 35926
    223     */
    224    private static class Visa implements CreditCardType {
    225        private static final String PREFIX = "4";
    226        @Override
    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 static class Amex implements CreditCardType {
    235        private static final String PREFIX = "34,37,";
    236        @Override
    237        public boolean matches(String card) {
    238            String prefix2 = card.substring(0, 2) + ",";
    239            return ((PREFIX.contains(prefix2)) && (card.length() == 15));
    240        }
    241    }
    242
    243    private static class Discover implements CreditCardType {
    244        private static final String PREFIX = "6011";
    245        @Override
    246        public boolean matches(String card) {
    247            return (card.substring(0, 4).equals(PREFIX) && (card.length() == 16));
    248        }
    249    }
    250
    251    private static class Mastercard implements CreditCardType {
    252        private static final String PREFIX = "51,52,53,54,55,";
    253        @Override
    254        public boolean matches(String card) {
    255            String prefix2 = card.substring(0, 2) + ",";
    256            return ((PREFIX.contains(prefix2)) && (card.length() == 16));
    257        }
    258    }
    259
    260}