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; 018 019 import java.util.ArrayList; 020 import java.util.Collection; 021 import java.util.Iterator; 022 023 import org.apache.commons.validator.util.Flags; 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: 594962 $ $Date: 2007-11-14 18:30:27 +0100 (Mi, 14. Nov 2007) $ 042 * @since Validator 1.1 043 * @deprecated Use the new CreditCardValidator in the routines package. This class 044 * will be removed in a future release. 045 */ 046 public class CreditCardValidator { 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 * @since Validator 1.1.2 059 */ 060 public static final int NONE = 0; 061 062 /** 063 * Option specifying that American Express cards are allowed. 064 */ 065 public static final int AMEX = 1 << 0; 066 067 /** 068 * Option specifying that Visa cards are allowed. 069 */ 070 public static final int VISA = 1 << 1; 071 072 /** 073 * Option specifying that Mastercard cards are allowed. 074 */ 075 public static final int MASTERCARD = 1 << 2; 076 077 /** 078 * Option specifying that Discover cards are allowed. 079 */ 080 public static final int DISCOVER = 1 << 3; 081 082 /** 083 * The CreditCardTypes that are allowed to pass validation. 084 */ 085 private Collection cardTypes = new ArrayList(); 086 087 /** 088 * Create a new CreditCardValidator with default options. 089 */ 090 public CreditCardValidator() { 091 this(AMEX + VISA + MASTERCARD + DISCOVER); 092 } 093 094 /** 095 * Create a new CreditCardValidator with the specified options. 096 * @param options Pass in 097 * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that 098 * those are the only valid card types. 099 */ 100 public CreditCardValidator(int options) { 101 super(); 102 103 Flags f = new Flags(options); 104 if (f.isOn(VISA)) { 105 this.cardTypes.add(new Visa()); 106 } 107 108 if (f.isOn(AMEX)) { 109 this.cardTypes.add(new Amex()); 110 } 111 112 if (f.isOn(MASTERCARD)) { 113 this.cardTypes.add(new Mastercard()); 114 } 115 116 if (f.isOn(DISCOVER)) { 117 this.cardTypes.add(new Discover()); 118 } 119 } 120 121 /** 122 * Checks if the field is a valid credit card number. 123 * @param card The card number to validate. 124 * @return Whether the card number is valid. 125 */ 126 public boolean isValid(String card) { 127 if ((card == null) || (card.length() < 13) || (card.length() > 19)) { 128 return false; 129 } 130 131 if (!this.luhnCheck(card)) { 132 return false; 133 } 134 135 Iterator types = this.cardTypes.iterator(); 136 while (types.hasNext()) { 137 CreditCardType type = (CreditCardType) types.next(); 138 if (type.matches(card)) { 139 return true; 140 } 141 } 142 143 return false; 144 } 145 146 /** 147 * Add an allowed CreditCardType that participates in the card 148 * validation algorithm. 149 * @param type The type that is now allowed to pass validation. 150 * @since Validator 1.1.2 151 */ 152 public void addAllowedCardType(CreditCardType type){ 153 this.cardTypes.add(type); 154 } 155 156 /** 157 * Checks for a valid credit card number. 158 * @param cardNumber Credit Card Number. 159 * @return Whether the card number passes the luhnCheck. 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 * CreditCardType implementations define how validation is performed 188 * for one type/brand of credit card. 189 * @since Validator 1.1.2 190 */ 191 public interface CreditCardType { 192 193 /** 194 * Returns true if the card number matches this type of credit 195 * card. Note that this method is <strong>not</strong> responsible 196 * for analyzing the general form of the card number because 197 * <code>CreditCardValidator</code> performs those checks before 198 * calling this method. It is generally only required to valid the 199 * length and prefix of the number to determine if it's the correct 200 * type. 201 * @param card The card number, never null. 202 * @return true if the number matches. 203 */ 204 boolean matches(String card); 205 206 } 207 208 /** 209 * Change to support Visa Carte Blue used in France 210 * has been removed - see Bug 35926 211 */ 212 private class Visa implements CreditCardType { 213 private static final String PREFIX = "4"; 214 public boolean matches(String card) { 215 return ( 216 card.substring(0, 1).equals(PREFIX) 217 && (card.length() == 13 || card.length() == 16)); 218 } 219 } 220 221 private class Amex implements CreditCardType { 222 private static final String PREFIX = "34,37,"; 223 public boolean matches(String card) { 224 String prefix2 = card.substring(0, 2) + ","; 225 return ((PREFIX.indexOf(prefix2) != -1) && (card.length() == 15)); 226 } 227 } 228 229 private class Discover implements CreditCardType { 230 private static final String PREFIX = "6011"; 231 public boolean matches(String card) { 232 return (card.substring(0, 4).equals(PREFIX) && (card.length() == 16)); 233 } 234 } 235 236 private class Mastercard implements CreditCardType { 237 private static final String PREFIX = "51,52,53,54,55,"; 238 public boolean matches(String card) { 239 String prefix2 = card.substring(0, 2) + ","; 240 return ((PREFIX.indexOf(prefix2) != -1) && (card.length() == 16)); 241 } 242 } 243 244 }