validateCardNumber function

CCNumValidationResults validateCardNumber(
  1. String ccNumStr, {
  2. bool luhnValidateUnionPay = false,
  3. bool ignoreLuhnValidation = 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,
  bool ignoreLuhnValidation = 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: UNKNOWN_CARD_TYPE,
        isValid: false,
        isPotentiallyValid: false,
        message: 'No input or contains non-numerical characters',
      );
    }

    List<CreditCardType> types = detectCCType(trimmedNumStr);
    // Card type couldn't be detected but it is still potentially valid
    if (types.isEmpty) {
      return CCNumValidationResults(
        ccType: UNKNOWN_CARD_TYPE,
        isValid: false,
        isPotentiallyValid: true,
        message: _DEFAULT_FAIL_MESSAGE
      );
    }
    else if (types.length > 1) {
      return CCNumValidationResults(
        ccType: UNKNOWN_CARD_TYPE,
        isValid: false,
        isPotentiallyValid: true,
        message: 'Multiple card types detected: [${types.map((e) => e.prettyType).join(", ")}]',
      );
    }

    CreditCardType type = types[0];
    int maxCardLength = type.lengths.reduce(max);

    bool isLuhnValid = false;
    bool isPotentiallyValid = false;
    if (ignoreLuhnValidation) {
      isLuhnValid = true;
    }
    else {
        // Check Luhn validity of the number if the conditions are met, usually Luhn validity is checked
      if (types.contains(CreditCardType.unionPay()) && luhnValidateUnionPay == false) {
        isLuhnValid = true;
      } else {
        isLuhnValid = checkLuhnValidity(trimmedNumStr);
      }
    }

    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 (type.lengths.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,
    );
  }