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 java.io.Serializable; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.List; 023 024import org.apache.commons.validator.routines.checkdigit.CheckDigit; 025import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit; 026 027/** 028 * Perform credit card validations. 029 * 030 * <p> 031 * By default, AMEX + VISA + MASTERCARD + DISCOVER card types are allowed. You can specify which 032 * cards should pass validation by configuring the validation options. For 033 * example, 034 * </p> 035 * 036 * <pre> 037 * <code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code> 038 * </pre> 039 * 040 * <p> 041 * configures the validator to only pass American Express and Visa cards. 042 * If a card type is not directly supported by this class, you can create an 043 * instance of the {@link CodeValidator} class and pass it to a {@link CreditCardValidator} 044 * constructor along with any existing validators. For example: 045 * </p> 046 * 047 * <pre> 048 * <code>CreditCardValidator ccv = new CreditCardValidator( 049 * new CodeValidator[] { 050 * CreditCardValidator.AMEX_VALIDATOR, 051 * CreditCardValidator.VISA_VALIDATOR, 052 * new CodeValidator("^(4)(\\d{12,18})$", LUHN_VALIDATOR) // add VPAY 053 * };</code> 054 * </pre> 055 * 056 * <p> 057 * Alternatively you can define a validator using the {@link CreditCardRange} class. 058 * For example: 059 * </p> 060 * 061 * <pre> 062 * <code>CreditCardValidator ccv = new CreditCardValidator( 063 * new CreditCardRange[]{ 064 * new CreditCardRange("300", "305", 14, 14), // Diners 065 * new CreditCardRange("3095", null, 14, 14), // Diners 066 * new CreditCardRange("36", null, 14, 14), // Diners 067 * new CreditCardRange("38", "39", 14, 14), // Diners 068 * new CreditCardRange("4", null, new int[] {13, 16}), // VISA 069 * } 070 * ); 071 * </code> 072 * </pre> 073 * <p> 074 * This can be combined with a list of {@code CodeValidator}s 075 * </p> 076 * <p> 077 * More information can be found in Michael Gilleland's essay 078 * <a href="http://web.archive.org/web/20120614072656/http://www.merriampark.com/anatomycc.htm">Anatomy of Credit Card Numbers</a>. 079 * </p> 080 * 081 * @since 1.4 082 */ 083public class CreditCardValidator implements Serializable { 084 085 /** 086 * Class that represents a credit card range. 087 * @since 1.6 088 */ 089 public static class CreditCardRange { 090 final String low; // e.g. 34 or 644 091 final String high; // e.g. 34 or 65 092 final int minLen; // e.g. 16 or -1 093 final int maxLen; // e.g. 19 or -1 094 final int lengths[]; // e.g. 16,18,19 095 096 /** 097 * Create a credit card range specifier for use in validation 098 * of the number syntax including the IIN range. 099 * <p> 100 * The low and high parameters may be shorter than the length 101 * of an IIN (currently 6 digits) in which case subsequent digits 102 * are ignored and may range from 0-9. 103 * </p> 104 * <p> 105 * The low and high parameters may be different lengths. 106 * e.g. Discover "644" and "65". 107 * </p> 108 * @param low the low digits of the IIN range 109 * @param high the high digits of the IIN range 110 * @param minLen the minimum length of the entire number 111 * @param maxLen the maximum length of the entire number 112 */ 113 public CreditCardRange(final String low, final String high, final int minLen, final int maxLen) { 114 this.low = low; 115 this.high = high; 116 this.minLen = minLen; 117 this.maxLen = maxLen; 118 this.lengths = null; 119 } 120 121 /** 122 * Create a credit card range specifier for use in validation 123 * of the number syntax including the IIN range. 124 * <p> 125 * The low and high parameters may be shorter than the length 126 * of an IIN (currently 6 digits) in which case subsequent digits 127 * are ignored and may range from 0-9. 128 * </p> 129 * <p> 130 * The low and high parameters may be different lengths. 131 * e.g. Discover "644" and "65". 132 * </p> 133 * @param low the low digits of the IIN range 134 * @param high the high digits of the IIN range 135 * @param lengths array of valid lengths 136 */ 137 public CreditCardRange(final String low, final String high, final int [] lengths) { 138 this.low = low; 139 this.high = high; 140 this.minLen = -1; 141 this.maxLen = -1; 142 this.lengths = lengths.clone(); 143 } 144 } 145 146 private static final long serialVersionUID = 5955978921148959496L; 147 148 private static final int MIN_CC_LENGTH = 12; // minimum allowed length 149 150 private static final int MAX_CC_LENGTH = 19; // maximum allowed length 151 152 /** 153 * Option specifying that no cards are allowed. This is useful if 154 * you want only custom card types to validate so you turn off the 155 * default cards with this option. 156 * 157 * <pre> 158 * <code> 159 * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE); 160 * v.addAllowedCardType(customType); 161 * v.isValid(aCardNumber); 162 * </code> 163 * </pre> 164 */ 165 public static final long NONE = 0; 166 167 /** 168 * Option specifying that American Express cards are allowed. 169 */ 170 public static final long AMEX = 1 << 0; 171 172 /** 173 * Option specifying that Visa cards are allowed. 174 */ 175 public static final long VISA = 1 << 1; 176 177 /** 178 * Option specifying that Mastercard cards are allowed. 179 */ 180 public static final long MASTERCARD = 1 << 2; 181 182 /** 183 * Option specifying that Discover cards are allowed. 184 */ 185 public static final long DISCOVER = 1 << 3; // CHECKSTYLE IGNORE MagicNumber 186 187 /** 188 * Option specifying that Diners cards are allowed. 189 */ 190 public static final long DINERS = 1 << 4; // CHECKSTYLE IGNORE MagicNumber 191 192 /** 193 * Option specifying that VPay (Visa) cards are allowed. 194 * @since 1.5.0 195 */ 196 public static final long VPAY = 1 << 5; // CHECKSTYLE IGNORE MagicNumber 197 198 /** 199 * Option specifying that Mastercard cards (pre Oct 2016 only) are allowed. 200 * @deprecated for use until Oct 2016 only 201 */ 202 @Deprecated 203 public static final long MASTERCARD_PRE_OCT2016 = 1 << 6; // CHECKSTYLE IGNORE MagicNumber 204 205 /** 206 * Luhn checkdigit validator for the card numbers. 207 */ 208 private static final CheckDigit LUHN_VALIDATOR = LuhnCheckDigit.LUHN_CHECK_DIGIT; 209 210 /** 211 * American Express (Amex) Card Validator 212 * <ul> 213 * <li>34xxxx (15)</li> 214 * <li>37xxxx (15)</li> 215 * </ul> 216 */ 217 public static final CodeValidator AMEX_VALIDATOR = new CodeValidator("^(3[47]\\d{13})$", LUHN_VALIDATOR); 218 219 /** 220 * Diners Card Validator 221 * <ul> 222 * <li>300xxx - 305xxx (14)</li> 223 * <li>3095xx (14)</li> 224 * <li>36xxxx (14)</li> 225 * <li>38xxxx (14)</li> 226 * <li>39xxxx (14)</li> 227 * </ul> 228 */ 229 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); 230 231 /** 232 * Discover Card regular expressions 233 * <ul> 234 * <li>6011xx (16)</li> 235 * <li>644xxx - 65xxxx (16)</li> 236 * </ul> 237 */ 238 private static final RegexValidator DISCOVER_REGEX = new RegexValidator("^(6011\\d{12,13})$", "^(64[4-9]\\d{13})$", "^(65\\d{14})$", "^(62[2-8]\\d{13})$"); 239 240 /** Discover Card Validator */ 241 public static final CodeValidator DISCOVER_VALIDATOR = new CodeValidator(DISCOVER_REGEX, LUHN_VALIDATOR); 242 243 /** 244 * Mastercard regular expressions 245 * <ul> 246 * <li>2221xx - 2720xx (16)</li> 247 * <li>51xxx - 55xxx (16)</li> 248 * </ul> 249 */ 250 private static final RegexValidator MASTERCARD_REGEX = new RegexValidator( 251 "^(5[1-5]\\d{14})$", // 51 - 55 (pre Oct 2016) 252 // valid from October 2016 253 "^(2221\\d{12})$", // 222100 - 222199 254 "^(222[2-9]\\d{12})$",// 222200 - 222999 255 "^(22[3-9]\\d{13})$", // 223000 - 229999 256 "^(2[3-6]\\d{14})$", // 230000 - 269999 257 "^(27[01]\\d{13})$", // 270000 - 271999 258 "^(2720\\d{12})$" // 272000 - 272099 259 ); 260 261 /** Mastercard Card Validator */ 262 public static final CodeValidator MASTERCARD_VALIDATOR = new CodeValidator(MASTERCARD_REGEX, LUHN_VALIDATOR); 263 264 /** 265 * Mastercard Card Validator (pre Oct 2016) 266 * @deprecated for use until Oct 2016 only 267 */ 268 @Deprecated 269 public static final CodeValidator MASTERCARD_VALIDATOR_PRE_OCT2016 = new CodeValidator("^(5[1-5]\\d{14})$", LUHN_VALIDATOR); 270 271 /** 272 * Visa Card Validator 273 * <p> 274 * 4xxxxx (13 or 16) 275 * </p> 276 */ 277 public static final CodeValidator VISA_VALIDATOR = new CodeValidator("^(4)(\\d{12}|\\d{15})$", LUHN_VALIDATOR); 278 279 /** 280 * VPay (Visa) Card Validator 281 * <p> 282 * 4xxxxx (13-19) 283 * </p> 284 * @since 1.5.0 285 */ 286 public static final CodeValidator VPAY_VALIDATOR = new CodeValidator("^(4)(\\d{12,18})$", LUHN_VALIDATOR); 287 288 // package protected for unit test access 289 static CodeValidator createRangeValidator(final CreditCardRange[] creditCardRanges, final CheckDigit digitCheck) { 290 return new CodeValidator( 291 // must be numeric (rest of validation is done later) 292 new RegexValidator("(\\d+)") { 293 private static final long serialVersionUID = 1L; 294 private final transient CreditCardRange[] ccr = creditCardRanges.clone(); 295 296 @Override 297 public boolean isValid(final String value) { 298 return validate(value) != null; 299 } 300 301 @Override 302 public String[] match(final String value) { 303 return new String[] { validate(value) }; 304 } 305 306 @Override 307 // must return full string 308 public String validate(final String value) { 309 if (super.match(value) != null) { 310 final int length = value.length(); 311 for (final CreditCardRange range : ccr) { 312 if (validLength(length, range)) { 313 if (range.high == null) { // single prefix only 314 if (value.startsWith(range.low)) { 315 return value; 316 } 317 } else if (range.low.compareTo(value) <= 0 // no need to trim value here 318 && 319 // here we have to ignore digits beyond the prefix 320 range.high.compareTo(value.substring(0, range.high.length())) >= 0) { 321 return value; 322 } 323 } 324 } 325 } 326 return null; 327 } 328 }, digitCheck); 329 } 330 331 /** 332 * Creates a new generic CreditCardValidator which validates the syntax and check digit only. 333 * Does not check the Issuer Identification Number (IIN) 334 * 335 * @return the validator 336 * @since 1.6 337 */ 338 public static CreditCardValidator genericCreditCardValidator() { 339 return genericCreditCardValidator(MIN_CC_LENGTH, MAX_CC_LENGTH); 340 } 341 342 /** 343 * Creates a new generic CreditCardValidator which validates the syntax and check digit only. 344 * Does not check the Issuer Identification Number (IIN) 345 * 346 * @param length exact length 347 * @return the validator 348 * @since 1.6 349 */ 350 public static CreditCardValidator genericCreditCardValidator(final int length) { 351 return genericCreditCardValidator(length, length); 352 } 353 354 /** 355 * Creates a new generic CreditCardValidator which validates the syntax and check digit only. 356 * Does not check the Issuer Identification Number (IIN) 357 * 358 * @param minLen minimum allowed length 359 * @param maxLen maximum allowed length 360 * @return the validator 361 * @since 1.6 362 */ 363 public static CreditCardValidator genericCreditCardValidator(final int minLen, final int maxLen) { 364 return new CreditCardValidator(new CodeValidator[] {new CodeValidator("(\\d+)", minLen, maxLen, LUHN_VALIDATOR)}); 365 } 366 367 // package protected for unit test access 368 static boolean validLength(final int valueLength, final CreditCardRange range) { 369 if (range.lengths != null) { 370 for (final int length : range.lengths) { 371 if (valueLength == length) { 372 return true; 373 } 374 } 375 return false; 376 } 377 return valueLength >= range.minLen && valueLength <= range.maxLen; 378 } 379 380 /** 381 * The CreditCardTypes that are allowed to pass validation. 382 */ 383 private final List<CodeValidator> cardTypes = new ArrayList<>(); 384 385 /** 386 * Constructs a new CreditCardValidator with default options. 387 * The default options are: 388 * AMEX, VISA, MASTERCARD and DISCOVER 389 */ 390 public CreditCardValidator() { 391 this(AMEX + VISA + MASTERCARD + DISCOVER); 392 } 393 394 /** 395 * Constructs a new CreditCardValidator with the specified {@link CodeValidator}s. 396 * @param creditCardValidators Set of valid code validators 397 */ 398 public CreditCardValidator(final CodeValidator[] creditCardValidators) { 399 if (creditCardValidators == null) { 400 throw new IllegalArgumentException("Card validators are missing"); 401 } 402 Collections.addAll(cardTypes, creditCardValidators); 403 } 404 405 /** 406 * Constructs a new CreditCardValidator with the specified {@link CodeValidator}s 407 * and {@link CreditCardRange}s. 408 * <p> 409 * This can be used to combine predefined validators such as {@link #MASTERCARD_VALIDATOR} 410 * with additional validators using the simpler {@link CreditCardRange}s. 411 * @param creditCardValidators Set of valid code validators 412 * @param creditCardRanges Set of valid code validators 413 * @since 1.6 414 */ 415 public CreditCardValidator(final CodeValidator[] creditCardValidators, final CreditCardRange[] creditCardRanges) { 416 if (creditCardValidators == null) { 417 throw new IllegalArgumentException("Card validators are missing"); 418 } 419 if (creditCardRanges == null) { 420 throw new IllegalArgumentException("Card ranges are missing"); 421 } 422 Collections.addAll(cardTypes, creditCardValidators); 423 Collections.addAll(cardTypes, createRangeValidator(creditCardRanges, LUHN_VALIDATOR)); 424 } 425 426 /** 427 * Constructs a new CreditCardValidator with the specified {@link CreditCardRange}s. 428 * @param creditCardRanges Set of valid code validators 429 * @since 1.6 430 */ 431 public CreditCardValidator(final CreditCardRange[] creditCardRanges) { 432 if (creditCardRanges == null) { 433 throw new IllegalArgumentException("Card ranges are missing"); 434 } 435 Collections.addAll(cardTypes, createRangeValidator(creditCardRanges, LUHN_VALIDATOR)); 436 } 437 438 /** 439 * Constructs a new CreditCardValidator with the specified options. 440 * @param options Pass in 441 * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that 442 * those are the only valid card types. 443 */ 444 public CreditCardValidator(final long options) { 445 if (isOn(options, VISA)) { 446 this.cardTypes.add(VISA_VALIDATOR); 447 } 448 449 if (isOn(options, VPAY)) { 450 this.cardTypes.add(VPAY_VALIDATOR); 451 } 452 453 if (isOn(options, AMEX)) { 454 this.cardTypes.add(AMEX_VALIDATOR); 455 } 456 457 if (isOn(options, MASTERCARD)) { 458 this.cardTypes.add(MASTERCARD_VALIDATOR); 459 } 460 461 if (isOn(options, MASTERCARD_PRE_OCT2016)) { 462 this.cardTypes.add(MASTERCARD_VALIDATOR_PRE_OCT2016); 463 } 464 465 if (isOn(options, DISCOVER)) { 466 this.cardTypes.add(DISCOVER_VALIDATOR); 467 } 468 469 if (isOn(options, DINERS)) { 470 this.cardTypes.add(DINERS_VALIDATOR); 471 } 472 } 473 474 /** 475 * Tests whether the given flag is on. If the flag is not a power of 2 476 * (ie. 3) this tests whether the combination of flags is on. 477 * 478 * @param options The options specified. 479 * @param flag Flag value to check. 480 * 481 * @return whether the specified flag value is on. 482 */ 483 private boolean isOn(final long options, final long flag) { 484 return (options & flag) > 0; 485 } 486 487 /** 488 * Checks if the field is a valid credit card number. 489 * @param card The card number to validate. 490 * @return Whether the card number is valid. 491 */ 492 public boolean isValid(final String card) { 493 if (card == null || card.isEmpty()) { 494 return false; 495 } 496 for (final CodeValidator cardType : cardTypes) { 497 if (cardType.isValid(card)) { 498 return true; 499 } 500 } 501 return false; 502 } 503 504 /** 505 * Checks if the field is a valid credit card number. 506 * @param card The card number to validate. 507 * @return The card number if valid or <code>null</code> 508 * if invalid. 509 */ 510 public Object validate(final String card) { 511 if (card == null || card.isEmpty()) { 512 return null; 513 } 514 Object result = null; 515 for (final CodeValidator cardType : cardTypes) { 516 result = cardType.validate(card); 517 if (result != null) { 518 return result; 519 } 520 } 521 return null; 522 523 } 524 525}