验证信用卡号码

时间:2014-05-27 11:50:42

标签: ios validation

我从用户那里取信用卡号作为输入。我想允许16个数字,并希望在每个4个数字后格式化空格。为此,我做了以下。

-(void)cardNumberValidation:(id)sender{
    UITextField *temp=sender;

    if ([temp.text length]>19) {
        txtCard.text= [temp.text substringToIndex:[temp.text length] - 1];
    }
    if ([temp.text length]==4) {
        txtCard.text=[NSString stringWithFormat:@"%@ ",temp.text];

        }
    if ([temp.text length]==9) {
        txtCard.text=[NSString stringWithFormat:@"%@ ",temp.text];
        }
    if ([temp.text length]==14) {
        NSString *lastChar = [txtCard.text substringFromIndex:[txtCard.text length] - 1];
        txtCard.text=[NSString stringWithFormat:@"%@ ",temp.text];
    }
}

但是现在当用户输入所有字符后将删除字符然后如果它将达到15个字符,然后用户将删除另外一个字符,那么此代码将在其后添加空格并且不允许进一步删除它。

任何人都可以就此或更好的方式向我提出建议。

8 个答案:

答案 0 :(得分:7)

在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
}
}

表单更多转到http://kalapun.com/posts/card-checking-in-swift/

答案 1 :(得分:4)

不要更改用户输入的文字,这只会引起混淆。不要让用户思考:WTF。用户以他理解的方式输入了号码,尽可能地尊重他。

只需清理用户输入的内容即可。通常只删除所有前导,训练和散布的空格字符,可能是任何非数字字符。然后确保输入的文本全部为数字且长度正确。

请注意,该号码的长度为13至19位,American Express为15位。请参阅:Bank card number

考虑代码:

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

如果用户在组之间输入了额外的空格字符,则最后一位数字将被删除。很容易想出这样的方案将避免所有可能的陷阱 示例:" 1234 4567 9012 3456"将被截断为" 1234 4567 9012 345"。

额外,Method to verify校验位:

+ (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");

输出:

  

检查数字有效:是

答案 2 :(得分:2)

我在我的应用程序中使用这个格式的信用卡

- (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];

}

答案 3 :(得分:1)

这是我的快速解决方案:

#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;
}

答案 4 :(得分:1)

如果你想保持目前的做法,我建议剥去所有空格,然后将它们重新插入正确的地方,例如:

-(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];
}

也就是说,更改用户文本的想法可能会让用户感到困惑,这种方法非常依赖于只接受VISA和/或万事达卡,因为其他发卡机构使用不同的格式。

答案 5 :(得分:1)

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
    }

答案 6 :(得分:0)

您应该使用非常强大且易于使用的API来完成此类任务。

Get Payment kit from GitHub

enter image description here

答案 7 :(得分:0)

您可以使用以下方法执行此操作:

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)            

    }


}

在上面的代码中,我给出了连字符。你可以用空格替换它。