1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.validator; 18 19 import java.util.ArrayList; 20 import java.util.Collection; 21 22 import org.apache.commons.validator.util.Flags; 23 24 /** 25 * Perform credit card validations. 26 * 27 * <p> 28 * By default, all supported card types are allowed. You can specify which 29 * cards should pass validation by configuring the validation options. For 30 * example, 31 * </p> 32 * 33 * <pre> 34 * <code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code> 35 * </pre> 36 * 37 * <p> 38 * configures the validator to only pass American Express and Visa cards. 39 * If a card type is not directly supported by this class, you can implement 40 * the CreditCardType interface and pass an instance into the 41 * <code>addAllowedCardType</code> method. 42 * </p> 43 * 44 * <p> 45 * For a similar implementation in Perl, reference Sean M. Burke's 46 * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>. 47 * More information is also available 48 * <a href="http://www.merriampark.com/anatomycc.htm">here</a>. 49 * </p> 50 * 51 * @since 1.1 52 * @deprecated Use the new CreditCardValidator in the routines package. This class 53 * will be removed in a future release. 54 */ 55 // CHECKSTYLE:OFF (deprecated code) 56 @Deprecated 57 public class CreditCardValidator { 58 59 private static class Amex implements CreditCardType { 60 static final Amex INSTANCE = new Amex(); 61 private static final String PREFIX = "34,37,"; 62 @Override 63 public boolean matches(final String card) { 64 final String prefix2 = card.substring(0, 2) + ","; 65 return PREFIX.contains(prefix2) && card.length() == 15; 66 } 67 } 68 69 /** 70 * CreditCardType implementations define how validation is performed 71 * for one type/brand of credit card. 72 * @since 1.1.2 73 */ 74 public interface CreditCardType { 75 76 /** 77 * Returns true if the card number matches this type of credit 78 * card. Note that this method is <strong>not</strong> responsible 79 * for analyzing the general form of the card number because 80 * <code>CreditCardValidator</code> performs those checks before 81 * calling this method. It is generally only required to valid the 82 * length and prefix of the number to determine if it's the correct 83 * type. 84 * @param card The card number, never null. 85 * @return true if the number matches. 86 */ 87 boolean matches(String card); 88 89 } 90 91 private static class Discover implements CreditCardType { 92 static final Discover INSTANCE = new Discover(); 93 private static final String PREFIX = "6011"; 94 @Override 95 public boolean matches(final String card) { 96 return card.substring(0, 4).equals(PREFIX) && card.length() == 16; 97 } 98 } 99 100 private static class Mastercard implements CreditCardType { 101 static final Mastercard INSTANCE = new Mastercard(); 102 private static final String PREFIX = "51,52,53,54,55,"; 103 @Override 104 public boolean matches(final String card) { 105 final String prefix2 = card.substring(0, 2) + ","; 106 return PREFIX.contains(prefix2) && card.length() == 16; 107 } 108 } 109 110 /** 111 * Change to support Visa Carte Blue used in France 112 * has been removed - see Bug 35926 113 */ 114 private static class Visa implements CreditCardType { 115 static final Visa INSTANCE = new Visa(); 116 private static final String PREFIX = "4"; 117 118 @Override 119 public boolean matches(final String card) { 120 return card.substring(0, 1).equals(PREFIX) && (card.length() == 13 || card.length() == 16); 121 } 122 } 123 124 /** 125 * Option specifying that no cards are allowed. This is useful if 126 * you want only custom card types to validate so you turn off the 127 * default cards with this option. 128 * <pre> 129 * <code> 130 * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE); 131 * v.addAllowedCardType(customType); 132 * v.isValid(aCardNumber); 133 * </code> 134 * </pre> 135 * @since 1.1.2 136 */ 137 public static final int NONE = 0; 138 139 /** 140 * Option specifying that American Express cards are allowed. 141 */ 142 public static final int AMEX = 1 << 0; 143 144 /** 145 * Option specifying that Visa cards are allowed. 146 */ 147 public static final int VISA = 1 << 1; 148 149 /** 150 * Option specifying that Mastercard cards are allowed. 151 */ 152 public static final int MASTERCARD = 1 << 2; 153 154 /** 155 * Option specifying that Discover cards are allowed. 156 */ 157 public static final int DISCOVER = 1 << 3; 158 159 /** 160 * The CreditCardTypes that are allowed to pass validation. 161 */ 162 private final Collection<CreditCardType> cardTypes = new ArrayList<>(); 163 164 /** 165 * Create a new CreditCardValidator with default options. 166 */ 167 public CreditCardValidator() { 168 this(AMEX + VISA + MASTERCARD + DISCOVER); 169 } 170 171 /** 172 * Creates a new CreditCardValidator with the specified options. 173 * @param options Pass in 174 * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that 175 * those are the only valid card types. 176 */ 177 public CreditCardValidator(final int options) { 178 final Flags f = new Flags(options); 179 if (f.isOn(VISA)) { 180 this.cardTypes.add(Visa.INSTANCE); 181 } 182 183 if (f.isOn(AMEX)) { 184 this.cardTypes.add(Amex.INSTANCE); 185 } 186 187 if (f.isOn(MASTERCARD)) { 188 this.cardTypes.add(Mastercard.INSTANCE); 189 } 190 191 if (f.isOn(DISCOVER)) { 192 this.cardTypes.add(Discover.INSTANCE); 193 } 194 } 195 196 /** 197 * Adds an allowed CreditCardType that participates in the card 198 * validation algorithm. 199 * @param type The type that is now allowed to pass validation. 200 * @since 1.1.2 201 */ 202 public void addAllowedCardType(final CreditCardType type){ 203 this.cardTypes.add(type); 204 } 205 206 /** 207 * Checks if the field is a valid credit card number. 208 * @param card The card number to validate. 209 * @return Whether the card number is valid. 210 */ 211 public boolean isValid(final String card) { 212 if (card == null || card.length() < 13 || card.length() > 19) { 213 return false; 214 } 215 if (!this.luhnCheck(card)) { 216 return false; 217 } 218 for (final Object cardType : this.cardTypes) { 219 final CreditCardType type = (CreditCardType) cardType; 220 if (type.matches(card)) { 221 return true; 222 } 223 } 224 return false; 225 } 226 227 /** 228 * Checks for a valid credit card number. 229 * @param cardNumber Credit Card Number. 230 * @return Whether the card number passes the luhnCheck. 231 */ 232 protected boolean luhnCheck(final String cardNumber) { 233 // number must be validated as 0..9 numeric first!! 234 final int digits = cardNumber.length(); 235 final int oddOrEven = digits & 1; 236 long sum = 0; 237 for (int count = 0; count < digits; count++) { 238 int digit = 0; 239 try { 240 digit = Integer.parseInt(cardNumber.charAt(count) + ""); 241 } catch (final NumberFormatException e) { 242 return false; 243 } 244 if ((count & 1 ^ oddOrEven) == 0) { // not 245 digit *= 2; 246 if (digit > 9) { 247 digit -= 9; 248 } 249 } 250 sum += digit; 251 } 252 return sum != 0 && sum % 10 == 0; 253 } 254 255 }