Skip to content

Commit

Permalink
Added cardholderName support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Rogers committed Nov 17, 2015
1 parent 9726410 commit 0906c12
Show file tree
Hide file tree
Showing 39 changed files with 257 additions and 98 deletions.
14 changes: 14 additions & 0 deletions CardIOCardholderNameTextFieldDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// CardIOConfigurableTextFieldDelegate.h
// See the file "LICENSE.md" for the full license governing this code.
//

#import "CardIOConfigurableTextFieldDelegate.h"

@interface CardIOCardholderNameTextFieldDelegate : CardIOConfigurableTextFieldDelegate {

}

+(BOOL)isValidCardholderName:(NSString*)cardholderName;

@end
24 changes: 24 additions & 0 deletions CardIOCardholderNameTextFieldDelegate.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// CardIOCardholderNameTextFieldDelegate.m
// See the file "LICENSE.md" for the full license governing this code.
//

#import "CardIOCardholderNameTextFieldDelegate.h"

@implementation CardIOCardholderNameTextFieldDelegate

-(id) init {
if ((self = [super init])) {
// Globalization: alphanumeric, space, hyphen are all definitely okay;
// there's no compelling reason for us to get fussy here.
self.numbersOnly = NO;
self.maxLength = 175; // PayPal REST APIs accept max of 175 chars for cardholder name
}
return self;
}

+(BOOL)isValidCardholderName:(NSString*)cardholderName {
return [cardholderName length] > 0;
}

@end
4 changes: 4 additions & 0 deletions CardIO_Public_API/CardIOCreditCardInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ typedef NS_ENUM(NSInteger, CardIOCreditCardType) {
/// @note May be nil, if postal code information was not requested.
@property(nonatomic, copy, readwrite) NSString *postalCode;

/// Cardholder Name.
/// @note May be nil, if cardholder name was not requested.
@property(nonatomic, copy, readwrite) NSString *cardholderName;

/// Was the card number scanned (as opposed to entered manually)?
@property(nonatomic, assign, readwrite) BOOL scanned;

Expand Down
3 changes: 3 additions & 0 deletions CardIO_Public_API/CardIOPaymentViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@
/// Set to YES if you need to collect the billing postal code. Defaults to NO.
@property(nonatomic, assign, readwrite) BOOL collectPostalCode;

/// Set to YES if you need to collect the cardholder name. Defaults to NO.
@property(nonatomic, assign, readwrite) BOOL collectCardholderName;

/// Set to NO if you don't want the camera to try to scan the card expiration.
/// Applies only if collectExpiry is also YES.
/// Defaults to YES.
Expand Down
1 change: 1 addition & 0 deletions Classes/CardIOContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@property(nonatomic, assign, readwrite) BOOL collectCVV;
@property(nonatomic, assign, readwrite) BOOL collectExpiry;
@property(nonatomic, assign, readwrite) BOOL collectPostalCode;
@property(nonatomic, assign, readwrite) BOOL collectCardholderName;
@property(nonatomic, assign, readwrite) BOOL disableManualEntryButtons;
@property(nonatomic, assign, readwrite) BOOL keepStatusBarStyle;
@property(nonatomic, assign, readwrite) UIBarStyle navigationBarStyle;
Expand Down
12 changes: 7 additions & 5 deletions Classes/CardIOCreditCardInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,14 @@ - (NSString *)redactedCardNumber {
}

- (NSString *)description {
return [NSString stringWithFormat:@"{%@ %@; expiry: %lu/%lu%@%@}",
return [NSString stringWithFormat:@"{%@ %@; expiry: %lu/%lu%@%@%@}",
[[self class] displayStringForCardType:self.cardType usingLanguageOrLocale:@"en"],
[self redactedCardNumber],
(unsigned long)self.expiryMonth,
(unsigned long)self.expiryYear,
([self.cvv length] ? [NSString stringWithFormat:@"; cvv: %@", self.cvv] : @""),
([self.postalCode length] ? [NSString stringWithFormat:@"; postal code: %@", self.postalCode] : @"")];
([self.cvv length] ? [NSString stringWithFormat:@"; cvv: %@", self.cvv] : @""),
([self.postalCode length] ? [NSString stringWithFormat:@"; postal code: %@", self.postalCode] : @""),
([self.cardholderName length] ? [NSString stringWithFormat:@"; cardholder name: %@", self.cardholderName] : @"")];
}

- (CardIOCreditCardType)cardType {
Expand All @@ -95,8 +96,9 @@ - (CardIOCreditCardInfo *)copyWithZone:(NSZone *)zone {
theCopy.cardNumber = self.cardNumber;
theCopy.expiryMonth = self.expiryMonth;
theCopy.expiryYear = self.expiryYear;
theCopy.cvv = self.cvv;
theCopy.postalCode = self.postalCode;
theCopy.cvv = self.cvv;
theCopy.postalCode = self.postalCode;
theCopy.cardholderName = self.cardholderName;
theCopy.scanned = self.scanned;
theCopy.cardImage = [self.cardImage copy];
return theCopy;
Expand Down
2 changes: 2 additions & 0 deletions Classes/CardIODataEntryViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@
@property(nonatomic, assign, readwrite) BOOL collectExpiry;
@property(nonatomic, assign, readwrite) BOOL collectCVV;
@property(nonatomic, assign, readwrite) BOOL collectPostalCode;
@property(nonatomic, assign, readwrite) BOOL collectCardholderName;
@property(nonatomic, strong, readwrite) UITextField *expiryTextField;
@property(nonatomic, strong, readwrite) UITextField *numberTextField;
@property(nonatomic, strong, readwrite) UITextField *cvvTextField;
@property(nonatomic, strong, readwrite) UITextField *postalCodeTextField;
@property(nonatomic, strong, readwrite) UITextField *cardholderNameTextField;

+ (BOOL)cardExpiryIsValid:(CardIOCreditCardInfo *)info;

Expand Down
173 changes: 114 additions & 59 deletions Classes/CardIODataEntryViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "CardIONumbersTextFieldDelegate.h"
#import "CardIOCVVTextFieldDelegate.h"
#import "CardIOPostalCodeTextFieldDelegate.h"
#import "CardIOCardholderNameTextFieldDelegate.h"
#import "CardIOCreditCardNumber.h"
#import "CardIORowBasedTableViewSection.h"
#import "CardIOSectionBasedTableViewDelegate.h"
Expand Down Expand Up @@ -50,6 +51,7 @@ - (NSString *)cvvPlaceholder;
@property(nonatomic, strong, readwrite) CardIOExpiryTextFieldDelegate* expiryTextFieldDelegate;
@property(nonatomic, strong, readwrite) CardIOCVVTextFieldDelegate *cvvRowTextFieldDelegate;
@property(nonatomic, strong, readwrite) CardIOPostalCodeTextFieldDelegate *postalCodeRowTextFieldDelegate;
@property(nonatomic, strong, readwrite) CardIOCardholderNameTextFieldDelegate *cardholderNameRowTextFieldDelegate;
@property(nonatomic, assign, readwrite) CGSize notificationSize;
@property(nonatomic, strong, readwrite) CardIOContext *context;
@property(nonatomic, assign, readwrite) CardIOCreditCardType cardTypeForLogo;
Expand Down Expand Up @@ -147,8 +149,9 @@ - (void)viewDidLoad {
self.navigationItem.rightBarButtonItem.enabled = NO;

self.collectExpiry = pvc.collectExpiry;
self.collectCVV = pvc.collectCVV;
self.collectPostalCode = pvc.collectPostalCode;
self.collectCVV = pvc.collectCVV;
self.collectPostalCode = pvc.collectPostalCode;
self.collectCardholderName = pvc.collectCardholderName;

self.scrollView = [[UIScrollView alloc] initWithFrame:self.relevantViewFrame];

Expand Down Expand Up @@ -290,33 +293,59 @@ - (void)viewDidLoad {
}

[rows addObject:multiFieldRow];
}

if(self.collectPostalCode) {
CardIOMultipleFieldTableViewCell *postalCodeRow = [[CardIOMultipleFieldTableViewCell alloc] init];
postalCodeRow.backgroundColor = kColorDefaultCell;
postalCodeRow.numberOfFields = 1;
postalCodeRow.hiddenLabels = YES;
postalCodeRow.textAlignment = [CardIOLocalizer textAlignmentForLanguageOrLocale:self.context.languageOrLocale];

NSString *postalCodeText = CardIOLocalizedString(@"entry_postal_code", self.context.languageOrLocale); // Postal Code
[postalCodeRow.labels addObject:postalCodeText];

self.postalCodeTextField = [postalCodeRow.textFields lastObject];
[self.visibleTextFields addObject:self.postalCodeTextField];

self.postalCodeRowTextFieldDelegate = [[CardIOPostalCodeTextFieldDelegate alloc] init];
self.postalCodeTextField.placeholder = postalCodeText;
self.postalCodeTextField.delegate = self.postalCodeRowTextFieldDelegate;
self.postalCodeTextField.text = self.cardInfo.postalCode;
self.postalCodeTextField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
self.postalCodeTextField.clearButtonMode = UITextFieldViewModeNever;
self.postalCodeTextField.text = @"";
self.postalCodeTextField.textAlignment = [CardIOLocalizer textAlignmentForLanguageOrLocale:self.context.languageOrLocale];
self.postalCodeTextField.autocorrectionType = UITextAutocorrectionTypeNo;

[rows addObject:postalCodeRow];
}
}

if(self.collectPostalCode) {
CardIOMultipleFieldTableViewCell *postalCodeRow = [[CardIOMultipleFieldTableViewCell alloc] init];
postalCodeRow.backgroundColor = kColorDefaultCell;
postalCodeRow.numberOfFields = 1;
postalCodeRow.hiddenLabels = YES;
postalCodeRow.textAlignment = [CardIOLocalizer textAlignmentForLanguageOrLocale:self.context.languageOrLocale];

NSString *postalCodeText = CardIOLocalizedString(@"entry_postal_code", self.context.languageOrLocale); // Postal Code
[postalCodeRow.labels addObject:postalCodeText];

self.postalCodeTextField = [postalCodeRow.textFields lastObject];
[self.visibleTextFields addObject:self.postalCodeTextField];

self.postalCodeRowTextFieldDelegate = [[CardIOPostalCodeTextFieldDelegate alloc] init];
self.postalCodeTextField.placeholder = postalCodeText;
self.postalCodeTextField.delegate = self.postalCodeRowTextFieldDelegate;
self.postalCodeTextField.text = self.cardInfo.postalCode;
self.postalCodeTextField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
self.postalCodeTextField.clearButtonMode = UITextFieldViewModeNever;
self.postalCodeTextField.text = @"";
self.postalCodeTextField.textAlignment = [CardIOLocalizer textAlignmentForLanguageOrLocale:self.context.languageOrLocale];
self.postalCodeTextField.autocorrectionType = UITextAutocorrectionTypeNo;

[rows addObject:postalCodeRow];
}

if(self.collectCardholderName) {
CardIOMultipleFieldTableViewCell *cardholderNameRow = [[CardIOMultipleFieldTableViewCell alloc] init];
cardholderNameRow.backgroundColor = kColorDefaultCell;
cardholderNameRow.numberOfFields = 1;
cardholderNameRow.hiddenLabels = YES;
cardholderNameRow.textAlignment = [CardIOLocalizer textAlignmentForLanguageOrLocale:self.context.languageOrLocale];

NSString *cardholderNameText = CardIOLocalizedString(@"entry_cardholder_name", self.context.languageOrLocale); // Cardholder Name
[cardholderNameRow.labels addObject:cardholderNameText];

self.cardholderNameTextField = [cardholderNameRow.textFields lastObject];
[self.visibleTextFields addObject:self.cardholderNameTextField];

self.cardholderNameRowTextFieldDelegate = [[CardIOCardholderNameTextFieldDelegate alloc] init];
self.cardholderNameTextField.placeholder = cardholderNameText;
self.cardholderNameTextField.delegate = self.cardholderNameRowTextFieldDelegate;
self.cardholderNameTextField.text = self.cardInfo.cardholderName;
self.cardholderNameTextField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;

This comment has been minimized.

Copy link
@bluk

bluk Nov 18, 2015

I believe this should be UIKeyboardTypeDefault.

self.cardholderNameTextField.clearButtonMode = UITextFieldViewModeNever;
self.cardholderNameTextField.text = @"";
self.cardholderNameTextField.textAlignment = [CardIOLocalizer textAlignmentForLanguageOrLocale:self.context.languageOrLocale];
self.cardholderNameTextField.autocorrectionType = UITextAutocorrectionTypeNo;

[rows addObject:cardholderNameRow];
}

CardIORowBasedTableViewSection *infoSection = [[CardIORowBasedTableViewSection alloc] init];
infoSection.rows = rows;
Expand Down Expand Up @@ -408,13 +437,19 @@ - (void)viewDidAppear:(BOOL)animated {
selector:@selector(cvvDidChange:)
name:UITextFieldTextDidChangeNotification
object:self.cvvTextField];
}
if(self.collectPostalCode) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(postalCodeDidChange:)
name:UITextFieldTextDidChangeNotification
object:self.postalCodeTextField];
}
}
if(self.collectPostalCode) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(postalCodeDidChange:)
name:UITextFieldTextDidChangeNotification
object:self.postalCodeTextField];
}
if(self.collectCardholderName) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cardholderNameDidChange:)
name:UITextFieldTextDidChangeNotification
object:self.cardholderNameTextField];
}

[self layoutForCurrentOrientation];

Expand Down Expand Up @@ -542,13 +577,15 @@ - (void)viewDidUnload {
self.tableViewDelegate = nil;
self.numberRowTextFieldDelegate = nil;
self.expiryTextFieldDelegate = nil;
self.cvvRowTextFieldDelegate = nil;
self.postalCodeRowTextFieldDelegate = nil;
self.cvvRowTextFieldDelegate = nil;
self.postalCodeRowTextFieldDelegate = nil;
self.cardholderNameRowTextFieldDelegate = nil;

self.expiryTextField = nil;
self.numberTextField = nil;
self.cvvTextField = nil;
self.postalCodeTextField = nil;
self.cvvTextField = nil;
self.postalCodeTextField = nil;
self.cardholderNameTextField = nil;

self.visibleTextFields = nil;

Expand Down Expand Up @@ -769,8 +806,9 @@ - (void)done {
self.cardInfo.cardNumber = [CardIOCreditCardNumber stringByRemovingNonNumbers:self.numberTextField.text];
}

self.cardInfo.cvv = self.cvvTextField.text;
self.cardInfo.postalCode = [self.postalCodeTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
self.cardInfo.cvv = self.cvvTextField.text;
self.cardInfo.postalCode = [self.postalCodeTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
self.cardInfo.cardholderName = self.cardholderNameTextField.text;

CardIOPaymentViewController *pvc = (CardIOPaymentViewController *)self.navigationController;
[pvc.paymentDelegate userDidProvideCreditCardInfo:self.cardInfo inPaymentViewController:pvc];
Expand Down Expand Up @@ -912,29 +950,46 @@ - (void)updateCvvColor {
}

- (void)postalCodeDidChange:(id)sender {
self.cardInfo.postalCode = [self.postalCodeTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

// For globalization, we can't be sure of a valid postalCode length. So for now we'll skip all of this.
//
// if([CardIOPostalCodeTextFieldDelegate isValidPostalCode:self.cardInfo.postalCode]) {
// [self advanceToNextEmptyFieldFrom:self.postalCodeTextField];
// self.postalCodeTextField.textColor = [CardIOTableViewCell defaultDetailTextLabelColorForCellStyle:[CardIOTableViewCell defaultCellStyle]];
// } else if(self.postalCodeTextField.text.length >= 5) {
// // probably won't reach this case, since length == 5 is the only validation rule, but we'll leave it here for consitency and for future enhancements.
// self.postalCodeTextField.textColor = [UIColor redColor];
// } else {
// self.postalCodeTextField.textColor = [CardIOTableViewCell defaultDetailTextLabelColorForCellStyle:[CardIOTableViewCell defaultCellStyle]];
// }
self.cardInfo.postalCode = [self.postalCodeTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

// For globalization, we can't be sure of a valid postalCode length. So for now we'll skip all of this.
//
// if([CardIOPostalCodeTextFieldDelegate isValidPostalCode:self.cardInfo.postalCode]) {
// [self advanceToNextEmptyFieldFrom:self.postalCodeTextField];
// self.postalCodeTextField.textColor = [CardIOTableViewCell defaultDetailTextLabelColorForCellStyle:[CardIOTableViewCell defaultCellStyle]];
// } else if(self.postalCodeTextField.text.length >= 5) {
// // probably won't reach this case, since length == 5 is the only validation rule, but we'll leave it here for consitency and for future enhancements.
// self.postalCodeTextField.textColor = [UIColor redColor];
// } else {
// self.postalCodeTextField.textColor = [CardIOTableViewCell defaultDetailTextLabelColorForCellStyle:[CardIOTableViewCell defaultCellStyle]];
// }

[self validate];
}

[self validate];
- (void)cardholderNameDidChange:(id)sender {
self.cardInfo.cardholderName = self.cardholderNameTextField.text;

if([CardIOCardholderNameTextFieldDelegate isValidCardholderName:self.cardInfo.cardholderName]) {
[self advanceToNextEmptyFieldFrom:self.cardholderNameTextField];

This comment has been minimized.

Copy link
@bluk

bluk Nov 18, 2015

Will need to test but I believe this method is called on every character entry so then it would always advance to the next field after entering in any character.

This comment has been minimized.

Copy link
@mgroger2

mgroger2 Nov 18, 2015

Good catches. Resolved both issues.

self.cardholderNameTextField.textColor = [CardIOTableViewCell defaultDetailTextLabelColorForCellStyle:[CardIOTableViewCell defaultCellStyle]];
} else if(self.cardholderNameTextField.text.length >= 175) {
// probably won't reach this case, since length == 175 is the only validation rule, but we'll leave it here for consitency and for future enhancements.
self.cardholderNameTextField.textColor = [UIColor redColor];
} else {
self.cardholderNameTextField.textColor = [CardIOTableViewCell defaultDetailTextLabelColorForCellStyle:[CardIOTableViewCell defaultCellStyle]];
}

[self validate];
}

- (BOOL)validate {
BOOL numberIsValid = !self.manualEntry || [CardIOCreditCardNumber isValidNumber:self.cardInfo.cardNumber];
BOOL expiryIsValid = !self.expiryTextField || [[self class] cardExpiryIsValid:self.cardInfo];
BOOL cvvIsValid = !self.cvvTextField || [CardIOCVVTextFieldDelegate isValidCVV:self.cardInfo.cvv forNumber:self.cardInfo.cardNumber];
BOOL postalCodeIsValid = !self.postalCodeTextField || [CardIOPostalCodeTextFieldDelegate isValidPostalCode:self.cardInfo.postalCode];
BOOL isValid = numberIsValid && expiryIsValid && cvvIsValid && postalCodeIsValid;
BOOL cvvIsValid = !self.cvvTextField || [CardIOCVVTextFieldDelegate isValidCVV:self.cardInfo.cvv forNumber:self.cardInfo.cardNumber];
BOOL postalCodeIsValid = !self.postalCodeTextField || [CardIOPostalCodeTextFieldDelegate isValidPostalCode:self.cardInfo.postalCode];
BOOL cardholderNameIsValid = !self.cardholderNameTextField || [CardIOCardholderNameTextFieldDelegate isValidCardholderName:self.cardInfo.cardholderName];
BOOL isValid = numberIsValid && expiryIsValid && cvvIsValid && postalCodeIsValid && cardholderNameIsValid;
self.navigationItem.rightBarButtonItem.enabled = isValid;

return isValid;
Expand Down
Loading

0 comments on commit 0906c12

Please sign in to comment.