[ACCEPTED]-Validate credit card number-validation

Accepted answer
Score: 16

In Swift

enum CardType: String {
    case Unknown, Amex, Visa, MasterCard, Diners, Discover, JCB, Elo, Hipercard, UnionPay

    static let allCards = [Amex, Visa, MasterCard, Diners, Discover, JCB, Elo, Hipercard, UnionPay]

    var regex : String {
        switch self {
        case .Amex:
           return "^3[47][0-9]{5,}$"
        case .Visa:
           return "^4[0-9]{6,}([0-9]{3})?$"
        case .MasterCard:
           return "^(5[1-5][0-9]{4}|677189)[0-9]{5,}$"
        case .Diners:
           return "^3(?:0[0-5]|[68][0-9])[0-9]{4,}$"
        case .Discover:
           return "^6(?:011|5[0-9]{2})[0-9]{3,}$"
        case .JCB:
           return "^(?:2131|1800|35[0-9]{3})[0-9]{3,}$"
        case .UnionPay:
           return "^(62|88)[0-9]{5,}$"
        case .Hipercard:
           return "^(606282|3841)[0-9]{5,}$"
        case .Elo:
           return "^((((636368)|(438935)|(504175)|(451416)|(636297))[0-9]{0,10})|((5067)|(4576)|(4011))[0-9]{0,12})$"
        default:
           return ""
        }
    }
}
extension UITextField{ 

func validateCreditCardFormat()-> (type: CardType, valid: Bool) {
        // Get only numbers from the input string
        var input = self.text!
        let numberOnly = input.stringByReplacingOccurrencesOfString("[^0-9]", withString: "", options: .RegularExpressionSearch)

    var type: CardType = .Unknown
    var formatted = ""
    var valid = false

    // detect card type
    for card in CardType.allCards {
        if (matchesRegex(card.regex, text: numberOnly)) {
            type = card
            break
        }
    }

    // check validity
    valid = luhnCheck(numberOnly)

    // format
    var formatted4 = ""
    for character in numberOnly.characters {
        if formatted4.characters.count == 4 {
            formatted += formatted4 + " "
            formatted4 = ""
        }
        formatted4.append(character)
    }

    formatted += formatted4 // the rest

    // return the tuple
    return (type, valid)
}

func matchesRegex(regex: String!, text: String!) -> Bool {
    do {
        let regex = try NSRegularExpression(pattern: regex, options: [.CaseInsensitive])
        let nsString = text as NSString
        let match = regex.firstMatchInString(text, options: [], range: NSMakeRange(0, nsString.length))
        return (match != nil)
    } catch {
        return false
    }
}

func luhnCheck(number: String) -> Bool {
    var sum = 0
    let digitStrings = number.characters.reverse().map { String($0) }

    for tuple in digitStrings.enumerate() {
        guard let digit = Int(tuple.element) else { return false }
        let odd = tuple.index % 2 == 1

        switch (odd, digit) {
        case (true, 9):
            sum += 9
        case (true, 0...8):
            sum += (digit * 2) % 9
        default:
            sum += digit
        }
    }

    return sum % 10 == 0
}
}

form more go to http://kalapun.com/posts/card-checking-in-swift/

0

Score: 5

Don't change the user's entered text, it 19 will just cause confusion. Don't cause the 18 user to think: WTF. The user entered the 17 number in the way he understood, honor that 16 as much as possible.

Just sanitize what the 15 user has entered. Generally just remove 14 all leading, training and interspersed space 13 characters, possibly any non-numeric characters. Then 12 ensure the entered text is all numeric and 11 of the correct length.

Keep in mind that 10 the number can have a length of 13 to 19 9 digits, American Express is 15 digits. See: Bank card number

Consider 8 the code:

if ([temp.text length]>19) {
    txtCard.text= [temp.text substringToIndex:[temp.text length] - 1];
}

If the user entered an extra space 7 character between groups the last digit 6 will be deleted. It is all to easy to come 5 up with such a scheme will avoid all possible 4 pitfalls.
Example: "1234 4567 9012 3 3456" would be truncated to "1234 2 4567 9012 345".

Extra, Method to verify the check digit:

+ (BOOL)isValidCheckDigitForCardNumberString:(NSString *)cardNumberString {
    int checkSum = 0;
    uint8_t *cardDigitArray = (uint8_t *)[cardNumberString dataUsingEncoding:NSUTF8StringEncoding].bytes;
    int digitsCount = (int)cardNumberString.length;
    BOOL odd = cardNumberString.length % 2;

    for (int digitIndex=0; digitIndex<digitsCount; digitIndex++) {
        uint8_t cardDigit = cardDigitArray[digitIndex] - '0';
        if (digitIndex % 2 == odd) {
            cardDigit = cardDigit * 2;
            cardDigit = cardDigit / 10 + cardDigit % 10;
        }
        checkSum += cardDigit;
    }

    return (checkSum % 10 == 0);
}

BOOL checkDigitValid = [TestClass isValidCheckDigitForCardNumberString:@"371238839571772"];
NSLog(@"check digit valid: %@", checkDigitMatch ? @"yes" : @"no");

Output:

check 1 digit valid: yes

Score: 2

i am using this one in one of my app for 1 credit card like format

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init] ; 
    if([string length]==0)
    {
        [formatter setGroupingSeparator:@"-"];
        [formatter setGroupingSize:4];
        [formatter setUsesGroupingSeparator:YES];
        [formatter setSecondaryGroupingSize:2];
        NSString *num = textField.text ;
        num= [num stringByReplacingOccurrencesOfString:@"-" withString:@""];
        NSString *str = [formatter stringFromNumber:[NSNumber numberWithDouble:[num doubleValue]]];
        [formatter release];
        textField.text=str;
        NSLog(@"%@",str);
        return YES;
    }
    else {
        [formatter setGroupingSeparator:@"-"];
        [formatter setGroupingSize:2];
        [formatter setUsesGroupingSeparator:YES];
        [formatter setSecondaryGroupingSize:2];
        NSString *num = textField.text ;
        if(![num isEqualToString:@""])
        {
            num= [num stringByReplacingOccurrencesOfString:@"-" withString:@""];
            NSString *str = [formatter stringFromNumber:[NSNumber numberWithDouble:[num doubleValue]]];
            [formatter release];
            textField.text=str;
        }

        //NSLog(@"%@",str);
        return YES;
    }

    //[formatter setLenient:YES];

}
Score: 1

Here is my quick solution:

#define kLENGTH 4

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if (string.length > 0) {
        NSUInteger length = textField.text.length;
        int cntr = (int)((length - (length/kLENGTH)) / kLENGTH);
        if (!(((length + 1) % kLENGTH) - cntr)) {
            NSString *str = [textField.text stringByAppendingString:[NSString stringWithFormat:@"%@ ", string]];
            textField.text = str;
            return NO;
        }
    } else {
        if ([textField.text hasSuffix:@" "]) {
            textField.text = [textField.text substringToIndex:textField.text.length - 2];
            return NO;
        }
    }
    return YES;
}

0

Score: 1

If you want to maintain your current approach, I'd 7 suggest stripping all the spaces out and 6 then reinserting them at the right places, something 5 like:

-(void)cardNumberValidation:(id)sender
{
    NSString*   text = [sender text];

    // Strip out all spaces
    text = [text stringByReplacingOccurrencesOfString:@" " withString:@""];

    // Truncate to 16 characters
    if(text.length)
        text = [text substringToIndex:16];

    // Insert spaces
    if(text.length > 12)
        text = [text stringByReplacingCharactersInRange:NSMakeRange(12, 0) withString:@" "];
    if(text.length > 8)
        text = [text stringByReplacingCharactersInRange:NSMakeRange(8, 0) withString:@" "];
    if(text.length > 4)
        text = [text stringByReplacingCharactersInRange:NSMakeRange(4, 0) withString:@" "];

    [sender setText:text];
}

That said, the idea of changing the 4 users text on them can be confusing to the 3 user, and this approach is very dependent 2 on only accepting VISA and/or MasterCard 1 as other card issuers use different formats.

Score: 1

Solution in Swift:

let z = 4, intervalString = " "

func canInsert(atLocation y:Int) -> Bool { return ((1 + y)%(z + 1) == 0) ? true : false }

func canRemove(atLocation y:Int) -> Bool { return (y != 0) ? (y%(z + 1) == 0) : false }

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

        let nsText = textField.text! as NSString

        if range.location == 19 { return false }

        if range.length == 0 && canInsert(atLocation: range.location) {
            textField.text! = textField.text! + intervalString + string
            return false
        }

        if range.length == 1 && canRemove(atLocation: range.location) {
            textField.text! = nsText.stringByReplacingCharactersInRange(NSMakeRange(range.location-1, 2), withString: "")
            return false
        }

        return true
    }

0

Score: 0

You can do this by using following:

class PaymentViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var textFieldCardNumber: UITextField!

 //for keeping track of cursor in text field for setting limit by Chetan
var cardNumberCursorPreviousPosition = 0


//MARK: - LifeCycle Methods
override func viewDidLoad() {
    super.viewDidLoad()

    self.textFieldCardNumber.delegate = self

    //for applying did change event on text fields
    self.textFieldCardNumber.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


func textFieldDidChange(textField: UITextField) {

    //logic for adding hyphen after each 4 digits
    if textField == self.textFieldCardNumber {

        if count(self.textFieldCardNumber.text) <= 7 {

            var cardType: String = cardValidator.checkCardType(self.textFieldCardNumber.text)
            println("Card Type : \(cardType)")

        }

        //for applying hyphen
        if (count(textFieldCardNumber.text) == 4  && cardNumberCursorPreviousPosition == 3) || (count(textFieldCardNumber.text) == 9 && cardNumberCursorPreviousPosition == 8) ||
            (count(textFieldCardNumber.text) == 14  && cardNumberCursorPreviousPosition == 13) {

            textFieldCardNumber.text = "\(textFieldCardNumber.text)-"

        }

        //for removing hyphen and its preceding character/number
        if (count(textFieldCardNumber.text) == 4 && cardNumberCursorPreviousPosition == 5) ||
            (count(textFieldCardNumber.text) == 9 && cardNumberCursorPreviousPosition == 10) ||
            (count(textFieldCardNumber.text) == 14 && cardNumberCursorPreviousPosition == 15) {

            textFieldCardNumber.text = textFieldCardNumber.text.substringToIndex(advance(textFieldCardNumber.text.endIndex, -1))

        }

         cardNumberCursorPreviousPosition = count(textFieldCardNumber.text)            

    }


}

In the 2 above code I have given hyphen. You can 1 replace it with space.

More Related questions