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}