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.List; 023import 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: 1227719 $ $Date: 2012-01-05 12:45:51 -0500 (Thu, 05 Jan 2012) $ 042 * @since Validator 1.4 043 */ 044public class CreditCardValidator implements Serializable { 045 046 private static final long serialVersionUID = 5955978921148959496L; 047 048 /** 049 * Option specifying that no cards are allowed. This is useful if 050 * you want only custom card types to validate so you turn off the 051 * default cards with this option. 052 * <br/> 053 * <pre> 054 * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE); 055 * v.addAllowedCardType(customType); 056 * v.isValid(aCardNumber); 057 * </pre> 058 */ 059 public static final long NONE = 0; 060 061 /** 062 * Option specifying that American Express cards are allowed. 063 */ 064 public static final long AMEX = 1 << 0; 065 066 /** 067 * Option specifying that Visa cards are allowed. 068 */ 069 public static final long VISA = 1 << 1; 070 071 /** 072 * Option specifying that Mastercard cards are allowed. 073 */ 074 public static final long MASTERCARD = 1 << 2; 075 076 /** 077 * Option specifying that Discover cards are allowed. 078 */ 079 public static final long DISCOVER = 1 << 3; 080 081 /** 082 * Option specifying that Diners cards are allowed. 083 */ 084 public static final long DINERS = 1 << 4; 085 086 /** 087 * The CreditCardTypes that are allowed to pass validation. 088 */ 089 private final List cardTypes = new ArrayList(); 090 091 /** 092 * Luhn checkdigit validator for the card numbers. 093 */ 094 private static final CheckDigit LUHN_VALIDATOR = LuhnCheckDigit.LUHN_CHECK_DIGIT; 095 096 /** American Express (Amex) Card Validator */ 097 public static final CodeValidator AMEX_VALIDATOR = new CodeValidator("^(3[47]\\d{13})$", LUHN_VALIDATOR); 098 099 /** 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}