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 }