【问题标题】:Validate credit card number验证信用卡号码
【发布时间】:2014-05-27 11:50:42
【问题描述】:

我从用户那里获取信用卡号作为输入。我想允许 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 个字符,然后用户将再删除一个字符,那么此代码将在其后添加空格并且不允许进一步删除它。

谁能给我建议或任何更好的方法。

【问题讨论】:

标签: ios validation


【解决方案1】:

在斯威夫特中

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/

【讨论】:

  • swift 3+ 版本?
【解决方案2】:

不要更改用户输入的文字,这只会造成混乱。不要让用户认为:WTF。用户以他理解的方式输入数字,尽可能尊重它。

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

请记住,号码的长度可以是 13 到 19 位,美国运通是 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");

输出:

校验位有效:是

【讨论】:

  • 支付工具包是最好的解决方案。link
  • 支付工具有限,您必须有他们的自定义文本字段,您不能将卡号、cvc 和到期数据拆分到单独的文本字段中
【解决方案3】:

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

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

}

【讨论】:

    【解决方案4】:

    这是我的快速解决方案:

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

    【讨论】:

      【解决方案5】:

      如果您想保持当前的方法,我建议去掉所有空格,然后将它们重新插入到正确的位置,例如:

      -(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 和/或万事达卡,因为其他发卡机构使用不同的格式。

      【讨论】:

      • 是的,我明白了,这就是为什么我停止实施这个并转向为所有类型的发卡机构构建的支付工具包
      【解决方案6】:

      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
          }
      

      【讨论】:

        【解决方案7】:

        您可以通过以下方式做到这一点:

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

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

        【讨论】:

        • cardValidator 实例和checkCardType 函数指的是什么?
        【解决方案8】:

        对于此类任务,您应该使用非常强大且易于使用的 API 支付工具包。

        Get Payment kit from GitHub

        【讨论】:

        • 它也有据可查。您将能够非常轻松地集成它。
        猜你喜欢
        • 1970-01-01
        • 2012-01-01
        • 1970-01-01
        • 2014-08-14
        • 2011-11-16
        • 1970-01-01
        • 2016-01-22
        • 2011-05-09
        • 1970-01-01
        相关资源
        最近更新 更多