Last active
June 27, 2019 10:04
-
-
Save cwagdev/e66d4806c1f63fe9387a to your computer and use it in GitHub Desktop.
Revisions
-
cwagdev revised this gist
Jul 17, 2015 . 1 changed file with 71 additions and 59 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,6 +40,76 @@ enum CreditCardType: Printable { return [] } } init(number: String) { if let first = number.prefixAsInt(1) where first == 4 { self = .Visa return } if let firstTwo = number.prefixAsInt(2) { if firstTwo == 35 { self = .JCB return } if contains([30, 36, 38, 39], firstTwo) { self = .DinersClub return } if 50...55 ~= firstTwo { self = .MasterCard return } if firstTwo == 34 || firstTwo == 37 { self = .Amex return } if firstTwo == 65 { self = .Discover return } } if let firstThree = number.prefixAsInt(3) where 644...649 ~= firstThree { self = .Discover return } if let firstFour = number.prefixAsInt(4) where firstFour == 6011 { self = .Discover return } if let firstSix = number.prefixAsInt(6) where 622126...622925 ~= firstSix { self = .Discover return } self = .Unknown } } private extension String { /** The first `length` characters of the String as an Int. :param: length The number of characters to return :returns: The first `length` characters of the String as an Int. `nil` if `length` exceed the length of the String or is not representable as Int. */ func prefixAsInt(length: Int) -> Int? { if count(self) < length { return nil } return substringWithRange(startIndex..<advance(startIndex, length)).toInt() } } /// Represents a String as a Credit Card @@ -49,7 +119,7 @@ struct CreditCard { let number: String /// The type of credit card, this is generally accurate once the first two numbers are provided var type: CreditCardType { return CreditCardType(number: number) } /// The last 4 numbers of the card, nil if the length is < 4 var last4: String? { @@ -103,62 +173,4 @@ struct CreditCard { init(string: String) { self.number = string } } -
cwagdev created this gist
Jul 17, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,164 @@ /// Describes a type of credit card for a subset of known types. enum CreditCardType: Printable { case Amex, DinersClub, Discover, JCB, MasterCard, Visa, Unknown var description: String { switch self { case .Amex: return "Amex" case .DinersClub: return "Diners Club" case .Discover: return "Discover" case .JCB: return "JCB" case .MasterCard: return "MasterCard" case .Visa: return "Visa" case .Unknown: return "Unknown" } } /// A set of lengths that are considered valid for the type var validLengths: Set<Int> { switch self { case .Amex: return [15] case .DinersClub: return [14, 15, 16] case .Discover: return [16] case .JCB: return [16] case .MasterCard: return [16] case .Visa: return [13, 16] case .Unknown: return [] } } } /// Represents a String as a Credit Card struct CreditCard { /// The credit card number represented as a String let number: String /// The type of credit card, this is generally accurate once the first two numbers are provided var type: CreditCardType { return cardType() } /// The last 4 numbers of the card, nil if the length is < 4 var last4: String? { if count(number) < 4 { return nil } return number.substringWithRange(advance(number.endIndex, -4)..<number.endIndex) } /// A display version of the credit card number var formattedString: String { return number } /// True when both `isValidLength` and `isValidLuhn` are true var isValid: Bool { return isValidLength && isValidLuhn } /// True when the length of the card number meets a required length for the card type var isValidLength: Bool { return contains(type.validLengths, count(number)) } /// True when the Luhn algorithm https://en.wikipedia.org/wiki/Luhn_algorithm succeeds var isValidLuhn: Bool { var sum = 0 let digitStrings = reverse(number).map { String($0) } for tuple in enumerate(digitStrings) { if let digit = tuple.element.toInt() { 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 } } else { return false } } return sum % 10 == 0 } init(string: String) { self.number = string } /** Return the first `length` characters in the number as an Int :param: length The number of characters to return as an Int :returns: The Int value or nil if the length is longer than the number or conversion to Int fails */ private func numberPrefix(length: Int) -> Int? { if count(number) < length { return nil } return number.substringWithRange(number.startIndex..<advance(number.startIndex, length)).toInt() } private func cardType() -> CreditCardType { if let first = numberPrefix(1) where first == 4 { return .Visa } if let firstTwo = numberPrefix(2) { if firstTwo == 35 { return .JCB } if contains([30, 36, 38, 39], firstTwo) { return .DinersClub } if 50...55 ~= firstTwo { return .MasterCard } if firstTwo == 34 || firstTwo == 37 { return .Amex } if firstTwo == 65 { return .Discover } } if let firstThree = numberPrefix(3) where 644...649 ~= firstThree { return .Discover } if let firstFour = numberPrefix(4) where firstFour == 6011 { return .Discover } if let firstSix = numberPrefix(6) where 622126...622925 ~= firstSix { return .Discover } return .Unknown } }