validateCardNumber function

CCNumValidationResults validateCardNumber(
  1. String ccNumStr, {
  2. bool luhnValidateUnionPay = false,
})

Validates a credit card number that is passed in as a string.

luhnValidateUnionPay: determines if this UnionPay card's number should be checked for Luhn validity. Default is to not check since some UnionPay cards do not use the Luhn algorithm.

Implementation

CCNumValidationResults validateCardNumber(String ccNumStr,
  {bool luhnValidateUnionPay = false}) {
    // Replace any whitespace or hyphens
    String trimmedNumStr = ccNumStr.replaceAll(whiteSpaceRegex, '');

    // If the str is empty or contains any non-numeric characters
    if (trimmedNumStr.isEmpty || trimmedNumStr.contains(nonNumberRegex)) {
      return CCNumValidationResults(
        ccType: CreditCardType.unknown,
        isValid: false,
        isPotentiallyValid: false,
        message: 'No input or contains non-numerical characters',
      );
    }

    CreditCardType type = detectCCType(trimmedNumStr);
    // Card type couldn't be detected but it is still potentially valid
    // TODO change? because then any unknown card could be potentially valid
    if (type == CreditCardType.unknown) {
      return CCNumValidationResults(
        ccType: type,
        isValid: false,
        isPotentiallyValid: true,
        message: _DEFAULT_FAIL_MESSAGE
      );
    }

    // Card number is longer than the industry standard of 19. Not valid nor potentially valid
    if (trimmedNumStr.length > _DEFAULT_MAX_CARD_NUM_LENGTH) {
      return CCNumValidationResults(
        ccType: type,
        isValid: false,
        isPotentiallyValid: false,
        message: 'Card number is greater than 19 digits',
      );
    }

    bool isLuhnValid = false;
    bool isPotentiallyValid = false;

    // Check Luhn validity of the number if the conditions are met, usually Luhn validity is checked
    if (type == CreditCardType.unionpay && luhnValidateUnionPay == false) {
      isLuhnValid = true;
    } else {
      isLuhnValid = checkLuhnValidity(trimmedNumStr);
    }

    int maxCardLength = _ccNumLengths.containsKey(type) ? _ccNumLengths[type]!.reduce(max) : _DEFAULT_MAX_CARD_NUM_LENGTH;
    String failedMessage = _DEFAULT_FAIL_MESSAGE;

    // Check if the card number length is viable.
    // If it is then decide the potential validity of this card number
    // The card number will be potentially valid if:
    //    The number is luhn valid OR the card number isn't complete yet
    if (_ccNumLengths[type]!.contains(trimmedNumStr.length)) {
      isPotentiallyValid = isLuhnValid || trimmedNumStr.length < maxCardLength;

      if (isLuhnValid && isPotentiallyValid) {
        failedMessage = ''; // Not a failed validation
      }

      return CCNumValidationResults(
        ccType: type,
        isValid: isLuhnValid,
        isPotentiallyValid: isPotentiallyValid,
        message: failedMessage,
      );
    }

    bool potentialForMoreDigits = trimmedNumStr.length < maxCardLength;
    if (potentialForMoreDigits) {
      failedMessage = ''; // Not an failed validation since there could be more digits being typed in
    }
    // Not a valid card but if the str passed in is 'incomplete' it is potentially valid
    // Incomplete means that the str passed in isn't as long as the max allowed card length
    return CCNumValidationResults(
      ccType: type,
      isValid: false,
      isPotentiallyValid: potentialForMoreDigits,
      message: failedMessage,
    );
  }