diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Main/MainViewController.h b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Main/MainViewController.h index bde5085f2..0adaac952 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Main/MainViewController.h +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Main/MainViewController.h @@ -31,9 +31,9 @@ @interface MainViewController (TableViewDelegates) /** -* A method that opens pbba payment screen from deeplink -* -* @param url - deeplink url -*/ + * A method that opens pbba payment screen from deeplink + * + * @param url - deeplink url + */ - (void)openPBBAScreen:(NSURL *)url; @end diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Main/MainViewController.m b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Main/MainViewController.m index fb02b8a62..a02d2290c 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Main/MainViewController.m +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Main/MainViewController.m @@ -22,10 +22,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#import "MaterialSnackbar.h" #import #import #import -#import "MaterialSnackbar.h" @import JudoKit_iOS; #import "ApplePayViewController.h" @@ -33,9 +33,9 @@ #import "ExampleAppStorage.h" #import "IASKAppSettingsViewController+Additions.h" #import "MainViewController.h" +#import "NoUICardPayViewController.h" #import "PBBAViewController.h" #import "PayWithCardTokenViewController.h" -#import "NoUICardPayViewController.h" #import "Settings.h" #import "UIViewController+Additions.h" @@ -265,7 +265,6 @@ - (void)serverToServerMethodOperation { } - (void)presentTextFieldAlertControllerWithCompletion:()completion { - } - (void)transactionDetailsMethodOperation { @@ -276,15 +275,15 @@ - (void)transactionDetailsMethodOperation { negativeButtonTitle:@"Cancel" textFieldPlaceholder:@"Receipt ID" andCompletion:^(NSString *text) { - if (!text || text.length == 0) { - return; - } - - [weakSelf.judoKit fetchTransactionWithReceiptId:text - completion:^(JPResponse *response, JPError *error) { - [weakSelf handleResponse:response error:error]; - }]; - }]; + if (!text || text.length == 0) { + return; + } + + [weakSelf.judoKit fetchTransactionWithReceiptId:text + completion:^(JPResponse *response, JPError *error) { + [weakSelf handleResponse:response error:error]; + }]; + }]; } // MARK: Helper methods @@ -331,20 +330,20 @@ - (JPConfiguration *)configuration { configuration.isInitialRecurringPayment = Settings.defaultSettings.isInitialRecurringPaymentEnabled; configuration.cardAddress = Settings.defaultSettings.address; configuration.primaryAccountDetails = Settings.defaultSettings.primaryAccountDetails; - + configuration.emailAddress = Settings.defaultSettings.emailAddress; configuration.phoneCountryCode = Settings.defaultSettings.phoneCountryCode; configuration.mobileNumber = Settings.defaultSettings.mobileNumber; configuration.scaExemption = Settings.defaultSettings.scaExemption; configuration.challengeRequestIndicator = Settings.defaultSettings.challengeRequestIndicator; configuration.threeDSTwoMaxTimeout = Settings.defaultSettings.threeDsTwoMaxTimeout; - + NSString *messageVersion = Settings.defaultSettings.threeDSTwoMessageVersion; - + if (messageVersion.length > 0) { configuration.threeDSTwoMessageVersion = messageVersion; } - + return configuration; } @@ -364,7 +363,7 @@ - (JPApplePayConfiguration *)applePayConfiguration { currency:Settings.defaultSettings.amount.currency countryCode:@"GB" paymentSummaryItems:items]; - + configuration.requiredShippingContactFields = Settings.defaultSettings.applePayShippingContactFields; configuration.requiredBillingContactFields = Settings.defaultSettings.applePayBillingContactFields; configuration.returnedContactInfo = Settings.defaultSettings.applePayReturnedContactInfo; @@ -410,17 +409,15 @@ - (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView - (void)tableView:(nonnull UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; DemoFeature *feature = self.features[indexPath.row]; - - BOOL isApplePayRelatedFeature = feature.type == DemoFeatureTypeApplePayPayment - || feature.type == DemoFeatureTypeApplePayPreAuth - || feature.type == DemoFeatureTypeApplePayStandalone; + + BOOL isApplePayRelatedFeature = feature.type == DemoFeatureTypeApplePayPayment || feature.type == DemoFeatureTypeApplePayPreAuth || feature.type == DemoFeatureTypeApplePayStandalone; JPConfiguration *configuration = self.configuration; if (isApplePayRelatedFeature && ![JudoKit isApplePayAvailableWithConfiguration:configuration]) { [self _jp_displayAlertWithTitle:@"Error" andMessage:@"ApplePay is not available for given configuration."]; return; } - + switch (feature.type) { case DemoFeatureTypePayment: [self paymentOperation]; diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/NoUICardPay/NoUICardPayViewController.m b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/NoUICardPay/NoUICardPayViewController.m index cfca0a39f..83c665142 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/NoUICardPay/NoUICardPayViewController.m +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/NoUICardPay/NoUICardPayViewController.m @@ -23,8 +23,8 @@ // SOFTWARE. #import "NoUICardPayViewController.h" -#import "UIViewController+Additions.h" #import "Settings.h" +#import "UIViewController+Additions.h" @import JudoKit_iOS; @@ -48,25 +48,25 @@ - (void)viewDidLoad { - (IBAction)payWithCardToken:(UIButton *)sender { self.configuration.reference = Settings.defaultSettings.reference; [self.payWithCardButton startLoading]; - + __weak typeof(self) weakSelf = self; [self.transactionService invokePaymentWithDetails:self.cardTransactionDetails andCompletion:^(JPResponse *response, JPError *error) { - [weakSelf.payWithCardButton stopLoading]; - [weakSelf handleResponse:response error:error showReceipt:true]; - }]; + [weakSelf.payWithCardButton stopLoading]; + [weakSelf handleResponse:response error:error showReceipt:true]; + }]; } - (IBAction)preAuthWithCardToken:(UIButton *)sender { self.configuration.reference = Settings.defaultSettings.reference; [self.preAuthWithCardButton startLoading]; - + __weak typeof(self) weakSelf = self; [self.transactionService invokePreAuthPaymentWithDetails:self.cardTransactionDetails andCompletion:^(JPResponse *response, JPError *error) { - [weakSelf.preAuthWithCardButton stopLoading]; - [weakSelf handleResponse:response error:error showReceipt:true]; - }]; + [weakSelf.preAuthWithCardButton stopLoading]; + [weakSelf handleResponse:response error:error showReceipt:true]; + }]; } - (JPCardTransactionDetails *)cardTransactionDetails { @@ -83,8 +83,9 @@ - (JPCardTransactionDetails *)cardTransactionDetails { address3:nil town:@"London" postCode:@"se151qa" - countryCode:@826]; - + countryCode:@826 + state:nil]; + return details; } @@ -105,7 +106,7 @@ - (JPCardTransactionService *)transactionService { isSandboxed:Settings.defaultSettings.isSandboxed andConfiguration:self.configuration]; } - return _transactionService; + return _transactionService; } @end diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Result/Model/Result.m b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Result/Model/Result.m index ea25d8682..96b3a84a3 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Result/Model/Result.m +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/Result/Model/Result.m @@ -112,7 +112,7 @@ + (instancetype)resultFromObject:(id)objectToBuildFrom { // Obtain the property name objc_property_t property = propertyArray[i]; NSString *name = [[NSString alloc] initWithUTF8String:property_getName(property)]; - + if (!name || name.length == 0 || [name isEqualToString:@"observationInfo"]) { break; } diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/TokenPay/PayWithCardTokenViewController.m b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/TokenPay/PayWithCardTokenViewController.m index 792712135..30d61fdcc 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/TokenPay/PayWithCardTokenViewController.m +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Controllers/TokenPay/PayWithCardTokenViewController.m @@ -100,11 +100,9 @@ - (void)_jp_keyboardWillHide:(NSNotification *)notification { - (IBAction)textFieldDidChange:(id)sender { BOOL shouldEnableButtons = - self.cardNetworkTextField.text.length > 0 - && self.cardTokenTextField.text.length > 0 - && self.cardholderNameTextField.text.length > 0; - - [self shouldEnableButtons: shouldEnableButtons]; + self.cardNetworkTextField.text.length > 0 && self.cardTokenTextField.text.length > 0 && self.cardholderNameTextField.text.length > 0; + + [self shouldEnableButtons:shouldEnableButtons]; } - (IBAction)addCardAction:(UIButton *)sender { @@ -112,9 +110,9 @@ - (IBAction)addCardAction:(UIButton *)sender { [self.judoKit invokeTransactionWithType:JPTransactionTypeSaveCard configuration:self.configuration completion:^(JPResponse *response, JPError *error) { - [weakSelf handleResponse:response error:error showReceipt:false]; - [weakSelf fillInFieldsFromResponse:response]; - }]; + [weakSelf handleResponse:response error:error showReceipt:false]; + [weakSelf fillInFieldsFromResponse:response]; + }]; } - (void)handleResponse:(JPResponse *)response error:(NSError *)error showReceipt:(BOOL)showReceipt { @@ -130,7 +128,7 @@ - (void)handleResponse:(JPResponse *)response error:(NSError *)error showReceipt - (void)fillInFieldsFromResponse:(JPResponse *)response { JPCardDetails *cardDetails = response.cardDetails; - + if (cardDetails) { self.cardNetworkTextField.text = [NSString stringWithFormat:@"%@", cardDetails.rawCardNetwork]; self.cardTokenTextField.text = cardDetails.cardToken; @@ -140,44 +138,44 @@ - (void)fillInFieldsFromResponse:(JPResponse *)response { - (IBAction)payWithCardToken:(UIButton *)sender { [self.payWithCardTokenButton startLoading]; - + __weak typeof(self) weakSelf = self; [self.transactionService invokeTokenPaymentWithDetails:self.cardTransactionDetails andCompletion:^(JPResponse *response, JPError *error) { - [weakSelf handleResponse:response error:error showReceipt:true]; - [weakSelf.payWithCardTokenButton stopLoading]; - }]; + [weakSelf handleResponse:response error:error showReceipt:true]; + [weakSelf.payWithCardTokenButton stopLoading]; + }]; } - (IBAction)preAuthWithCardToken:(UIButton *)sender { [self.preAuthWithCardTokenButton startLoading]; - + __weak typeof(self) weakSelf = self; [self.transactionService invokePreAuthTokenPaymentWithDetails:self.cardTransactionDetails andCompletion:^(JPResponse *response, JPError *error) { - [weakSelf handleResponse:response error:error showReceipt:true]; - [weakSelf.preAuthWithCardTokenButton stopLoading]; - }]; + [weakSelf handleResponse:response error:error showReceipt:true]; + [weakSelf.preAuthWithCardTokenButton stopLoading]; + }]; } - (JPCardTransactionDetails *)cardTransactionDetails { self.configuration.reference = Settings.defaultSettings.reference; - + JPCardTransactionDetails *details = [JPCardTransactionDetails detailsWithConfiguration:self.configuration]; - + NSString *secureCode = nil; - + if (self.cardSecurityCodeTextField.text.length > 0) { secureCode = self.cardSecurityCodeTextField.text; } - + details.cardToken = self.cardTokenTextField.text; details.cardType = @(self.cardNetworkTextField.text.integerValue)._jp_toCardNetworkType; details.secureCode = secureCode; details.cardholderName = self.cardholderNameTextField.text; - + return details; } @@ -192,7 +190,7 @@ - (JPCardTransactionService *)transactionService { isSandboxed:Settings.defaultSettings.isSandboxed andConfiguration:self.configuration]; } - return _transactionService; + return _transactionService; } @end diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Extensions/IASKAppSettingsViewController+Additions.m b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Extensions/IASKAppSettingsViewController+Additions.m index 74e5af90a..5fd375b48 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Extensions/IASKAppSettingsViewController+Additions.m +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Extensions/IASKAppSettingsViewController+Additions.m @@ -44,6 +44,7 @@ - (NSSet *)computeHiddenKeysWithPriority:(NSArray *)keys { kAddressTownKey, kAddressPostCodeKey, kAddressCountryCodeKey, + kAddressStateKey, kAddressPhoneCountryCodeKey, kAddressMobileNumberKey, kAddressEmailAddressKey, @@ -72,6 +73,7 @@ - (NSSet *)computeHiddenKeysWithPriority:(NSArray *)keys { kAddressTownKey, kAddressPostCodeKey, kAddressCountryCodeKey, + kAddressStateKey, kAddressPhoneCountryCodeKey, kAddressMobileNumberKey, kAddressEmailAddressKey, diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Extensions/UIViewController+Additions.m b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Extensions/UIViewController+Additions.m index f8309f3df..53c5c9eef 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Extensions/UIViewController+Additions.m +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Extensions/UIViewController+Additions.m @@ -19,7 +19,7 @@ - (void)presentTextFieldAlertControllerWithTitle:(NSString *)title negativeButtonTitle:(NSString *)negativeButton textFieldPlaceholder:(NSString *)placeholder andCompletion:(void (^)(NSString *text))completion { - + __block UITextField *textField = [UITextField new]; UIAlertController *controller = [UIAlertController alertControllerWithTitle:title message:message @@ -28,14 +28,14 @@ - (void)presentTextFieldAlertControllerWithTitle:(NSString *)title UIAlertAction *buttonOk = [UIAlertAction actionWithTitle:positiveButton style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { - completion(textField.text); - }]; + completion(textField.text); + }]; UIAlertAction *buttonCancel = [UIAlertAction actionWithTitle:negativeButton style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) { - completion(nil); - }]; + completion(nil); + }]; [controller addTextFieldWithConfigurationHandler:^(UITextField *aTextField) { textField = aTextField; diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Models/Settings.h b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Models/Settings.h index b7a491dc4..18551dccf 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Models/Settings.h +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Models/Settings.h @@ -28,6 +28,7 @@ static NSString *const kAddressLine3Key = @"address_line_3"; static NSString *const kAddressTownKey = @"address_town"; static NSString *const kAddressPostCodeKey = @"address_post_code"; static NSString *const kAddressCountryCodeKey = @"address_country_code"; +static NSString *const kAddressStateKey = @"address_state"; static NSString *const kAddressPhoneCountryCodeKey = @"address_phone_country_code"; static NSString *const kAddressMobileNumberKey = @"address_mobile_number"; static NSString *const kAddressEmailAddressKey = @"address_email_address"; diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Models/Settings.m b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Models/Settings.m index 9975ed042..7aad6957f 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Models/Settings.m +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Models/Settings.m @@ -123,7 +123,7 @@ - (NSString *)applePayMerchantId { - (JPReturnedInfo)applePayReturnedContactInfo { JPReturnedInfo fields = JPReturnedInfoNone; - + if ([self.defaults boolForKey:kIsApplePayBillingContactInfoRequired]) { fields |= JPReturnedInfoBillingContacts; } @@ -131,13 +131,13 @@ - (JPReturnedInfo)applePayReturnedContactInfo { if ([self.defaults boolForKey:kIsApplePayShippingContactInfoRequired]) { fields |= JPReturnedInfoShippingContacts; } - + return fields; } - (JPContactField)applePayBillingContactFields { JPContactField fields = JPContactFieldNone; - + if ([self.defaults boolForKey:kIsBillingContactFieldPostalAddressRequiredKey]) { fields |= JPContactFieldPostalAddress; } @@ -145,21 +145,21 @@ - (JPContactField)applePayBillingContactFields { if ([self.defaults boolForKey:kIsBillingContactFieldPhoneRequiredKey]) { fields |= JPContactFieldPhone; } - + if ([self.defaults boolForKey:kIsBillingContactFieldEmailRequiredKey]) { fields |= JPContactFieldEmail; } - + if ([self.defaults boolForKey:kIsBillingContactFieldNameRequiredKey]) { fields |= JPContactFieldName; } - + return fields; } - (JPContactField)applePayShippingContactFields { JPContactField fields = JPContactFieldNone; - + if ([self.defaults boolForKey:kIsShippingContactFieldPostalAddressRequiredKey]) { fields |= JPContactFieldPostalAddress; } @@ -167,19 +167,18 @@ - (JPContactField)applePayShippingContactFields { if ([self.defaults boolForKey:kIsShippingContactFieldPhoneRequiredKey]) { fields |= JPContactFieldPhone; } - + if ([self.defaults boolForKey:kIsShippingContactFieldEmailRequiredKey]) { fields |= JPContactFieldEmail; } - + if ([self.defaults boolForKey:kIsShippingContactFieldNameRequiredKey]) { fields |= JPContactFieldName; } - + return fields; } - #pragma mark - Supported card networks section - (JPCardNetworkType)supportedCardNetworks { @@ -275,7 +274,8 @@ - (JPAddress *)address { address3:[self.defaults stringForKey:kAddressLine3Key] town:[self.defaults stringForKey:kAddressTownKey] postCode:[self.defaults stringForKey:kAddressPostCodeKey] - countryCode:addressCountryCode]; + countryCode:addressCountryCode + state:[self.defaults stringForKey:kAddressStateKey]]; } return nil; } diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Settings.bundle/Root.plist b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Settings.bundle/Root.plist index 79c34495e..e31d70514 100644 --- a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Settings.bundle/Root.plist +++ b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Settings.bundle/Root.plist @@ -434,6 +434,16 @@ DefaultValue 826 + + Type + PSTextFieldSpecifier + Title + address_state_title + Key + address_state + DefaultValue + + Type PSTextFieldSpecifier diff --git a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Settings.bundle/en.lproj/Root.strings b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Settings.bundle/en.lproj/Root.strings index a6eb9d064..8b0adb8f0 100644 Binary files a/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Settings.bundle/en.lproj/Root.strings and b/Examples/ObjectiveCExampleApp/ObjectiveCExampleApp/Settings.bundle/en.lproj/Root.strings differ diff --git a/JudoKit_iOS.xcodeproj/project.pbxproj b/JudoKit_iOS.xcodeproj/project.pbxproj index 58f805f2d..74b812e1d 100644 --- a/JudoKit_iOS.xcodeproj/project.pbxproj +++ b/JudoKit_iOS.xcodeproj/project.pbxproj @@ -425,6 +425,8 @@ ADB514BF286DF03900332E47 /* JPThreeDSecureTwo.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB514BD286DF03900332E47 /* JPThreeDSecureTwo.m */; }; ADB514C0286DF03900332E47 /* JPThreeDSecureTwo.h in Headers */ = {isa = PBXBuildFile; fileRef = ADB514BE286DF03900332E47 /* JPThreeDSecureTwo.h */; }; ADB5FB46282CF72500B0E4BE /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADA50D79282C35DE00846943 /* OpenSSL.xcframework */; }; + ADC5AC0228EF30340049CAE3 /* JPState.m in Sources */ = {isa = PBXBuildFile; fileRef = ADC5AC0128EF30340049CAE3 /* JPState.m */; }; + ADC5AC0428EF30610049CAE3 /* JPState.h in Headers */ = {isa = PBXBuildFile; fileRef = ADC5AC0328EF30500049CAE3 /* JPState.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -862,6 +864,8 @@ ADB514B9286DED4200332E47 /* JPCReqParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPCReqParameters.m; sourceTree = ""; }; ADB514BD286DF03900332E47 /* JPThreeDSecureTwo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPThreeDSecureTwo.m; sourceTree = ""; }; ADB514BE286DF03900332E47 /* JPThreeDSecureTwo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPThreeDSecureTwo.h; sourceTree = ""; }; + ADC5AC0128EF30340049CAE3 /* JPState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JPState.m; sourceTree = ""; }; + ADC5AC0328EF30500049CAE3 /* JPState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JPState.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1670,6 +1674,7 @@ 3000727623F6AB540099F3C5 /* ConsumerDevice */, 3000731123FA9BBC0099F3C5 /* ContactInformation */, 3000728C23F6AB550099F3C5 /* Country */, + ADC5AC0028EF30010049CAE3 /* State */, ADB514B7286DED4200332E47 /* CReqParameters */, 3000726E23F6AB540099F3C5 /* DictionaryConvertible */, 300072B423F6AB560099F3C5 /* EnchancedPaymentDetail */, @@ -3009,6 +3014,15 @@ path = ThreeDSecureTwo; sourceTree = ""; }; + ADC5AC0028EF30010049CAE3 /* State */ = { + isa = PBXGroup; + children = ( + ADC5AC0328EF30500049CAE3 /* JPState.h */, + ADC5AC0128EF30340049CAE3 /* JPState.m */, + ); + path = State; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -3090,6 +3104,7 @@ 4492115F2469DC7200DF6D14 /* JPTheme.h in Headers */, 300072DD23F6AB560099F3C5 /* JPConstants.h in Headers */, 0779B1792490EC05000844AF /* JPTransactionButton.h in Headers */, + ADC5AC0428EF30610049CAE3 /* JPState.h in Headers */, 4492117C246A04C500DF6D14 /* JPPaymentMethodsCardListHeaderCellDelegate.h in Headers */, 525FF3DC252C41CF001F030E /* JPCardScanPreviewLayer.h in Headers */, 30D363A024B5BB1E0029BC86 /* JPBasicAuthorization.h in Headers */, @@ -3397,6 +3412,7 @@ 30006F3523F697B60099F3C5 /* JPKeychainService.m in Sources */, 30006F3723F697B60099F3C5 /* JPCardValidationService.m in Sources */, 3000732023FA9BBD0099F3C5 /* JPPostalAddress.m in Sources */, + ADC5AC0228EF30340049CAE3 /* JPState.m in Sources */, 3000735123FACF540099F3C5 /* JPUIConfiguration.m in Sources */, 30006F9C23F6993A0099F3C5 /* UIStackView+Additions.m in Sources */, 30D61D6C23BB741A004E5CEE /* JPPaymentMethodsInteractor.m in Sources */, diff --git a/JudoKit_iOSTests/Models/JPAddress/JPAddressTests.swift b/JudoKit_iOSTests/Models/JPAddress/JPAddressTests.swift index b1fd88e4f..babde1f72 100644 --- a/JudoKit_iOSTests/Models/JPAddress/JPAddressTests.swift +++ b/JudoKit_iOSTests/Models/JPAddress/JPAddressTests.swift @@ -30,7 +30,8 @@ class JPAddressTests: XCTestCase { "town": "town", "billingCountry": "billingCountry", "postCode": "postCode", - "countryCode": 123] as [String: Any] + "countryCode": 123, + "state": "CA"] as [String: Any] var address: JPAddress! @@ -52,7 +53,8 @@ class JPAddressTests: XCTestCase { address3: "line3", town: "town", postCode: "postCode", - countryCode: 123) + countryCode: 123, + state: "CA") XCTAssertEqual(address.address1, "line1") XCTAssertEqual(address.address2, "line2") @@ -60,6 +62,7 @@ class JPAddressTests: XCTestCase { XCTAssertEqual(address.town, "town") XCTAssertEqual(address.postCode, "postCode") XCTAssertEqual(address.countryCode, 123) + XCTAssertEqual(address.state, "CA") } /* @@ -76,5 +79,6 @@ class JPAddressTests: XCTestCase { XCTAssertEqual(address.town, "town") XCTAssertEqual(address.postCode, "postCode") XCTAssertEqual(address.countryCode, 123) + XCTAssertEqual(address.state, "CA") } } diff --git a/JudoKit_iOSTests/Models/Requests/JPPaymentRequestTests.swift b/JudoKit_iOSTests/Models/Requests/JPPaymentRequestTests.swift index 38e7b5c5e..a916ae28b 100644 --- a/JudoKit_iOSTests/Models/Requests/JPPaymentRequestTests.swift +++ b/JudoKit_iOSTests/Models/Requests/JPPaymentRequestTests.swift @@ -73,7 +73,8 @@ class JPPaymentRequestTests: XCTestCase { address3: "Line 3", town: "Town", postCode: "Postcode", - countryCode: 123) + countryCode: 123, + state: "CA") let paymentRequest = JPPaymentRequest(configuration: configuration, andCardDetails: cardDetails) @@ -96,5 +97,6 @@ class JPPaymentRequestTests: XCTestCase { XCTAssertEqual(paymentRequest.cardAddress?.town, cardDetails.cardAddress?.town) XCTAssertEqual(paymentRequest.cardAddress?.countryCode, cardDetails.cardAddress?.countryCode) XCTAssertEqual(paymentRequest.cardAddress?.postCode, cardDetails.cardAddress?.postCode) + XCTAssertEqual(paymentRequest.cardAddress?.state, cardDetails.cardAddress?.state) } } diff --git a/JudoKit_iOSTests/Modules/Transaction/Mocks/JPTransactionInteractorMock.swift b/JudoKit_iOSTests/Modules/Transaction/Mocks/JPTransactionInteractorMock.swift index 7639e309e..bad949956 100644 --- a/JudoKit_iOSTests/Modules/Transaction/Mocks/JPTransactionInteractorMock.swift +++ b/JudoKit_iOSTests/Modules/Transaction/Mocks/JPTransactionInteractorMock.swift @@ -124,6 +124,10 @@ class JPTransactionInteractorMock: JPTransactionInteractor { validationService.validateCountryInput(input) } + func validateStateInput(_ input: String) -> JPValidationResult { + validationService.validateStateInput(input) + } + func validatePostalCodeInput(_ input: String) -> JPValidationResult { validationService.validatePostalCodeInput(input) } diff --git a/Resources/en.lproj/Localizable.strings b/Resources/en.lproj/Localizable.strings index 24228ced4..73370faaf 100644 --- a/Resources/en.lproj/Localizable.strings +++ b/Resources/en.lproj/Localizable.strings @@ -33,6 +33,8 @@ "card_holder_email_hint" = "Email"; "card_holder_hint" = "Cardholder Name"; "card_holder_country_hint" = "Country"; +"card_holder_state_hint" = "State"; +"card_holder_province_hint" = "Province or territory"; "card_holder_phone_hint" = "Mobile number"; "card_holder_city_hint" = "City"; "card_holder_adress_line_hint" = "Address line %@"; @@ -77,6 +79,8 @@ "invalid_postcode" = "Invalid postcode entered"; "invalid_zip_code" = "Invalid ZIP code entered"; "invalid_email_address" = "Please enter a valid email"; +"invalid_state_should_not_be_empty" = "Please select a state"; +"invalid_province_territory_should_not_be_empty" = "Please select a province or territory"; "invalid_address" = "Please enter a valid address"; "invalid_mobile_number" = "Please enter a valid mobile number"; "invalid_city" = "Please enter a valid city"; diff --git a/Resources/es.lproj/Localizable.strings b/Resources/es.lproj/Localizable.strings index 86e10d063..5398ec4af 100644 --- a/Resources/es.lproj/Localizable.strings +++ b/Resources/es.lproj/Localizable.strings @@ -32,11 +32,13 @@ "back" = "Back"; "card_holder_email_hint" = "Email"; "card_holder_hint" = "Nombre del titular de la tarejeta"; -"card_holder_country_hint" = "Country"; +"card_holder_country_hint" = "País"; +"card_holder_state_hint" = "Estado"; +"card_holder_province_hint" = "Provincia o territorio"; "card_holder_phone_hint" = "Mobile number"; "card_holder_city_hint" = "City"; "card_holder_adress_line_hint" = "Address line %@"; -"card_number_hint" = "Numero de tarjeta "; +"card_number_hint" = "Numero de tarjeta"; "card_subtitle" = "%@ Terminación"; "cards" = "Tarjetas "; "check_card" = "Comprobar tarjeta"; @@ -77,6 +79,8 @@ "invalid_postcode" = "Código postal inválido"; "invalid_zip_code" = "Código postal inválido"; "invalid_email_address" = "Please enter a valid email"; +"invalid_state_should_not_be_empty" = "Por favor seleccione un estado"; +"invalid_province_territory_should_not_be_empty" = "Por favor seleccione una provincia o territorio"; "invalid_address" = "Please enter a valid address"; "invalid_mobile_number" = "Please enter a valid mobile number"; "invalid_city" = "Please enter a valid city"; diff --git a/Resources/fr.lproj/Localizable.strings b/Resources/fr.lproj/Localizable.strings index 88f6273b7..dcdaaf26b 100644 --- a/Resources/fr.lproj/Localizable.strings +++ b/Resources/fr.lproj/Localizable.strings @@ -31,7 +31,9 @@ "cancel" = "Anuler"; "back" = "Back"; "card_holder_hint" = "Nom du titulaire de la carte"; -"card_holder_country_hint" = "Country"; +"card_holder_country_hint" = "Pays"; +"card_holder_state_hint" = "État"; +"card_holder_province_hint" = "Province ou territoire"; "card_holder_phone_hint" = "Mobile number"; "card_holder_city_hint" = "City"; "card_holder_adress_line_hint" = "Address line %@"; @@ -77,6 +79,8 @@ "invalid_postcode" = "Code postal invalide"; "invalid_zip_code" = "Code postal invalide"; "invalid_email_address" = "Please enter a valid email"; +"invalid_state_should_not_be_empty" = "Veuillez sélectionner un état"; +"invalid_province_territory_should_not_be_empty" = "Veuillez sélectionner une province ou un territoire"; "invalid_address" = "Please enter a valid address"; "invalid_mobile_number" = "Please enter a valid mobile number"; "invalid_city" = "Please enter a valid city"; diff --git a/Source/JudoKit_iOS.h b/Source/JudoKit_iOS.h index 4c6eb4485..f9b36f0f2 100644 --- a/Source/JudoKit_iOS.h +++ b/Source/JudoKit_iOS.h @@ -63,6 +63,7 @@ #import "JPSDKInfo.h" #import "JPSection.h" #import "JPSession.h" +#import "JPState.h" #import "JPStoredCardDetails.h" #import "JPThemable.h" #import "JPTheme.h" diff --git a/Source/Models/Address/JPAddress.h b/Source/Models/Address/JPAddress.h index 549f30245..374a2acb4 100644 --- a/Source/Models/Address/JPAddress.h +++ b/Source/Models/Address/JPAddress.h @@ -59,6 +59,11 @@ */ @property (nonatomic, strong, nullable) NSNumber *countryCode; +/** + * Sets state code of the address. + */ +@property (nonatomic, strong, nullable) NSString *state; + /** * Designated Initializer * @@ -68,6 +73,7 @@ * @param town - a string that represents the town name * @param postCode the postal code of the card * @param countryCode - the country code of the address + * @param state - the state code of the address * * @return a JPAddress object */ @@ -76,7 +82,8 @@ address3:(nullable NSString *)address3 town:(nullable NSString *)town postCode:(nullable NSString *)postCode - countryCode:(nullable NSNumber *)countryCode; + countryCode:(nullable NSNumber *)countryCode + state:(nullable NSString *)state; /** * Initializer diff --git a/Source/Models/Address/JPAddress.m b/Source/Models/Address/JPAddress.m index 4827945bf..c4d3c83ab 100644 --- a/Source/Models/Address/JPAddress.m +++ b/Source/Models/Address/JPAddress.m @@ -31,7 +31,8 @@ - (instancetype)initWithAddress1:(nullable NSString *)address1 address3:(nullable NSString *)address3 town:(nullable NSString *)town postCode:(nullable NSString *)postCode - countryCode:(nullable NSNumber *)countryCode { + countryCode:(nullable NSNumber *)countryCode + state:(nullable NSString *)state { if (self = [super init]) { self.address1 = address1; @@ -40,6 +41,7 @@ - (instancetype)initWithAddress1:(nullable NSString *)address1 self.town = town; self.postCode = postCode; self.countryCode = countryCode; + self.state = state; } return self; } @@ -69,6 +71,10 @@ - (instancetype)initWithDictionary:(NSDictionary *)dictionary { if (dictionary[@"countryCode"]) { self.countryCode = dictionary[@"countryCode"]; } + + if (dictionary[@"state"]) { + self.state = dictionary[@"state"]; + } } return self; } diff --git a/Source/Models/Constants/JPConstants.h b/Source/Models/Constants/JPConstants.h index 5af8bda7e..a538fc993 100644 --- a/Source/Models/Constants/JPConstants.h +++ b/Source/Models/Constants/JPConstants.h @@ -97,4 +97,7 @@ static int const kDefaultThreeDSTwoMaxTimeout = 60; // Default 3DS 2.0 protocol message version static NSString *const kThreeDSTwoMessageVersionTwoDotTwo = @"2.2.0"; +static NSString *const kAlpha2CodeCanada = @"CA"; +static NSString *const kAlpha2CodeUSA = @"US"; +static NSString *const kAlpha2CodeUK = @"GB"; #endif /* JPConstants_h */ diff --git a/Source/Models/Request/JPApplePayRequest.m b/Source/Models/Request/JPApplePayRequest.m index 77a4b0594..d0f4520af 100644 --- a/Source/Models/Request/JPApplePayRequest.m +++ b/Source/Models/Request/JPApplePayRequest.m @@ -25,6 +25,7 @@ #import "JPApplePayRequest.h" #import "JPAddress.h" #import "JPCountry.h" +#import "JPState.h" #import @implementation JPApplePayPaymentToken @@ -116,13 +117,17 @@ - (void)populateApplePayMetadataWithPayment:(PKPayment *)payment { if (billingContact.postalAddress) { CNPostalAddress *postalAddress = billingContact.postalAddress; + JPCountry *country = [JPCountry forCountryName:postalAddress.country]; + NSNumber *countryCode = country ? @([country.numericCode intValue]) : nil; + NSString *state = country ? [JPState forStateName:postalAddress.state andCountryCode:country.alpha2Code].alpha2Code : nil; self.cardAddress = [[JPAddress alloc] initWithAddress1:postalAddress.street address2:postalAddress.city address3:postalAddress.postalCode town:postalAddress.city postCode:postalAddress.postalCode - countryCode:[JPCountry isoCodeForCountry:postalAddress.country]]; + countryCode:countryCode + state:state]; } } diff --git a/Source/Models/State/JPState.h b/Source/Models/State/JPState.h new file mode 100644 index 000000000..daa8ed244 --- /dev/null +++ b/Source/Models/State/JPState.h @@ -0,0 +1,57 @@ +// +// JPState.h +// JudoKit_iOS +// +// Copyright (c) 2022 Alternative Payments Ltd +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef JPState_h +#define JPState_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JPState : NSObject + +@property (nonatomic, readonly) NSString *alpha2Code; +@property (nonatomic, readonly) NSString *name; + ++ (nullable JPState *)forStateName:(nonnull NSString *)stateName andCountryCode:(nonnull NSString *)countryCode; + +- (nullable instancetype)initWithDictionary:(nullable NSDictionary *)dict; + +@end + +@interface JPStateList : NSObject + +@property (nonatomic, nullable, copy) NSArray *states; + ++ (instancetype)usStateList; + ++ (instancetype)caStateList; + +- (nullable instancetype)initWith:(nullable NSArray *)array; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* JPState_h */ diff --git a/Source/Models/State/JPState.m b/Source/Models/State/JPState.m new file mode 100644 index 000000000..07ef4c1b6 --- /dev/null +++ b/Source/Models/State/JPState.m @@ -0,0 +1,148 @@ +// +// JPState.m +// JudoKit_iOS +// +// Copyright (c) 2022 Alternative Payments Ltd +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#import "JPState.h" +#import "JPConstants.h" + +@implementation JPState + ++ (nullable JPState *)forStateName:(nonnull NSString *)stateName andCountryCode:(nonnull NSString *)countryCode { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", stateName]; + if ([countryCode isEqualToString:kAlpha2CodeUSA]) { + return [JPStateList.usStateList.states filteredArrayUsingPredicate:predicate].firstObject; + } else if ([countryCode isEqualToString:kAlpha2CodeCanada]) { + return [JPStateList.caStateList.states filteredArrayUsingPredicate:predicate].firstObject; + } + return nil; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dict { + if (!dict) { + return nil; + } + if (self = [super init]) { + _alpha2Code = dict[@"isoCode"]; + _name = dict[@"name"]; + } + return self; +} + +@end + +@implementation JPStateList + ++ (instancetype)usStateList { + return [[JPStateList alloc] initWith:@[ + @{@"isoCode" : @"AK", @"name" : @"Alaska"}, + @{@"isoCode" : @"AZ", @"name" : @"Arizona"}, + @{@"isoCode" : @"AR", @"name" : @"Arkansas"}, + @{@"isoCode" : @"CA", @"name" : @"California"}, + @{@"isoCode" : @"CO", @"name" : @"Colorado"}, + @{@"isoCode" : @"CT", @"name" : @"Connecticut"}, + @{@"isoCode" : @"DE", @"name" : @"Delaware"}, + @{@"isoCode" : @"AL", @"name" : @"Alabama"}, + @{@"isoCode" : @"DC", @"name" : @"District Of Columbia"}, + @{@"isoCode" : @"FL", @"name" : @"Florida"}, + @{@"isoCode" : @"GA", @"name" : @"Georgia"}, + @{@"isoCode" : @"HI", @"name" : @"Hawaii"}, + @{@"isoCode" : @"ID", @"name" : @"Idaho"}, + @{@"isoCode" : @"IL", @"name" : @"Illinois"}, + @{@"isoCode" : @"IN", @"name" : @"Indiana"}, + @{@"isoCode" : @"IA", @"name" : @"Iowa"}, + @{@"isoCode" : @"KS", @"name" : @"Kansas"}, + @{@"isoCode" : @"KY", @"name" : @"Kentucky"}, + @{@"isoCode" : @"LA", @"name" : @"Louisiana"}, + @{@"isoCode" : @"ME", @"name" : @"Maine"}, + @{@"isoCode" : @"MD", @"name" : @"Maryland"}, + @{@"isoCode" : @"MA", @"name" : @"Massachusetts"}, + @{@"isoCode" : @"MI", @"name" : @"Michigan"}, + @{@"isoCode" : @"MN", @"name" : @"Minnesota"}, + @{@"isoCode" : @"MS", @"name" : @"Mississippi"}, + @{@"isoCode" : @"MO", @"name" : @"Missouri"}, + @{@"isoCode" : @"MT", @"name" : @"Montana"}, + @{@"isoCode" : @"NE", @"name" : @"Nebraska"}, + @{@"isoCode" : @"NV", @"name" : @"Nevada"}, + @{@"isoCode" : @"NH", @"name" : @"New Hampshire"}, + @{@"isoCode" : @"NJ", @"name" : @"New Jersey"}, + @{@"isoCode" : @"NM", @"name" : @"New Mexico"}, + @{@"isoCode" : @"NY", @"name" : @"New York"}, + @{@"isoCode" : @"NC", @"name" : @"North Carolina"}, + @{@"isoCode" : @"ND", @"name" : @"North Dakota"}, + @{@"isoCode" : @"OH", @"name" : @"Ohio"}, + @{@"isoCode" : @"OK", @"name" : @"Oklahoma"}, + @{@"isoCode" : @"OR", @"name" : @"Oregon"}, + @{@"isoCode" : @"PA", @"name" : @"Pennsylvania"}, + @{@"isoCode" : @"RI", @"name" : @"Rhode Island"}, + @{@"isoCode" : @"SC", @"name" : @"South Carolina"}, + @{@"isoCode" : @"SD", @"name" : @"South Dakota"}, + @{@"isoCode" : @"TN", @"name" : @"Tennessee"}, + @{@"isoCode" : @"TX", @"name" : @"Texas"}, + @{@"isoCode" : @"UT", @"name" : @"Utah"}, + @{@"isoCode" : @"VT", @"name" : @"Vermont"}, + @{@"isoCode" : @"VA", @"name" : @"Virginia"}, + @{@"isoCode" : @"WA", @"name" : @"Washington"}, + @{@"isoCode" : @"WV", @"name" : @"West Virginia"}, + @{@"isoCode" : @"WI", @"name" : @"Wisconsin"}, + @{@"isoCode" : @"WY", @"name" : @"Wyoming"} + ]]; +} + ++ (instancetype)caStateList { + return [[JPStateList alloc] initWith:@[ + @{@"isoCode" : @"AB", @"name" : @"Alberta"}, + @{@"isoCode" : @"BC", @"name" : @"British Columbia"}, + @{@"isoCode" : @"MB", @"name" : @"Manitoba"}, + @{@"isoCode" : @"NB", @"name" : @"New Brunswick"}, + @{@"isoCode" : @"NL", @"name" : @"Newfoundland and Labrador"}, + @{@"isoCode" : @"NS", @"name" : @"Nova Scotia"}, + @{@"isoCode" : @"NT", @"name" : @"Northwest Territories"}, + @{@"isoCode" : @"NU", @"name" : @"Nunavut"}, + @{@"isoCode" : @"ON", @"name" : @"Ontario"}, + @{@"isoCode" : @"PE", @"name" : @"Prince Edward Island"}, + @{@"isoCode" : @"QC", @"name" : @"Quebec"}, + @{@"isoCode" : @"SK", @"name" : @"Saskatchewan"}, + @{@"isoCode" : @"YT", @"name" : @"Yukon"} + ]]; +} + +- (instancetype)initWith:(NSArray *)array { + if (!array) { + return nil; + } + if (self = [super init]) { + NSMutableArray *states = [NSMutableArray new]; + for (NSDictionary *stateDict in array) { + if (stateDict) { + JPState *state = [[JPState alloc] initWithDictionary:stateDict]; + if (state) { + [states addObject:state]; + } + } + } + self.states = states; + } + return self; +} + +@end diff --git a/Source/Modules/Transaction/Interactor/JPTransactionInteractor.h b/Source/Modules/Transaction/Interactor/JPTransactionInteractor.h index b613eec0a..0f9ac17d0 100644 --- a/Source/Modules/Transaction/Interactor/JPTransactionInteractor.h +++ b/Source/Modules/Transaction/Interactor/JPTransactionInteractor.h @@ -169,6 +169,15 @@ typedef NS_OPTIONS(NSUInteger, JPCardNetworkType); */ - (JPValidationResult *)validateCountryInput:(NSString *)input; +/** + * A method for validating the state + * + * @param input - the input state name string + * + * @returns a JPValidationResult with the validation status details + */ +- (JPValidationResult *)validateStateInput:(NSString *)input; + /** * A method for validating the post code number * diff --git a/Source/Modules/Transaction/Interactor/JPTransactionInteractor.m b/Source/Modules/Transaction/Interactor/JPTransactionInteractor.m index 2fe8486ac..e73b13c71 100644 --- a/Source/Modules/Transaction/Interactor/JPTransactionInteractor.m +++ b/Source/Modules/Transaction/Interactor/JPTransactionInteractor.m @@ -272,6 +272,10 @@ - (JPValidationResult *)validateCountryInput:(NSString *)input { return [self.cardValidationService validateCountryInput:input]; } +- (JPValidationResult *)validateStateInput:(NSString *)input { + return [self.cardValidationService validateStateInput:input]; +} + - (JPValidationResult *)validatePostalCodeInput:(NSString *)input { return [self.cardValidationService validatePostalCodeInput:input]; } diff --git a/Source/Modules/Transaction/Presenter/JPTransactionPresenter.m b/Source/Modules/Transaction/Presenter/JPTransactionPresenter.m index ef4d1d952..205cfffaf 100644 --- a/Source/Modules/Transaction/Presenter/JPTransactionPresenter.m +++ b/Source/Modules/Transaction/Presenter/JPTransactionPresenter.m @@ -33,11 +33,13 @@ #import "JPError+Additions.h" #import "JPInputType.h" #import "JPResponse.h" +#import "JPState.h" #import "JPTransactionInteractor.h" #import "JPTransactionRouter.h" #import "JPTransactionViewController.h" #import "JPValidationResult.h" #import "NSString+Additions.h" +#import "JPConstants.h" @interface JPTransactionPresenterImpl () @property (nonatomic, strong) JPTransactionViewModel *transactionViewModel; @@ -49,6 +51,7 @@ @interface JPTransactionPresenterImpl () @property (nonatomic, assign) BOOL isEmailValid; @property (nonatomic, assign) BOOL isPhoneCodeValid; @property (nonatomic, assign) BOOL isCountryNameValid; +@property (nonatomic, assign) BOOL isStateNameValid; @property (nonatomic, assign) BOOL isCityNameValid; @property (nonatomic, assign) BOOL isPhoneNumberValid; @property (nonatomic, assign) BOOL isAddressLine1Valid; @@ -71,6 +74,7 @@ - (instancetype)init { self.isEmailValid = NO; self.isPhoneCodeValid = NO; self.isCountryNameValid = YES; + self.isStateNameValid = YES; self.isCityNameValid = NO; self.isPhoneNumberValid = NO; self.isAddressLine1Valid = NO; @@ -109,6 +113,9 @@ - (void)prepareInitialViewModel { self.transactionViewModel.cardholderPhoneCodeViewModel.text = country.dialCode; } + self.transactionViewModel.statePickerViewModel.placeholder = @"card_holder_state_hint"._jp_localized; + self.transactionViewModel.pickerStates = @[]; + self.transactionViewModel.postalCodeInputViewModel.placeholder = @"post_code_hint"._jp_localized; NSString *buttonTitle = [self transactionButtonTitleForType:type]; @@ -152,6 +159,9 @@ - (void)handleInputChange:(NSString *)input [self updateCountryViewModelForInput:input showError:showError]; [self updatePostalCodeViewModelForInput:@"" showError:showError]; break; + case JPInputTypeCardholderState: + [self updateStateViewModelForInput:input showError:showError]; + break; case JPInputTypeCardPostalCode: [self updatePostalCodeViewModelForInput:input showError:showError]; break; @@ -319,7 +329,7 @@ - (void)updateTransactionButtonModelIfNeeded { self.transactionViewModel.addCardButtonViewModel.isEnabled = self.isSecureCodeValid; break; case JPCardDetailsModeThreeDS2BillingDetails: { - BOOL is3DS2Valid = self.isEmailValid && self.isCountryNameValid && self.isAddressLine1Valid && self.isAddressLine2Valid && self.isAddressLine3Valid && self.isPhoneNumberValid && self.isCityNameValid && self.isPostalCodeValid; + BOOL is3DS2Valid = self.isEmailValid && self.isCountryNameValid && self.isStateNameValid && self.isAddressLine1Valid && self.isAddressLine2Valid && self.isAddressLine3Valid && self.isPhoneNumberValid && self.isCityNameValid && self.isPostalCodeValid; self.transactionViewModel.addCardButtonViewModel.isEnabled = is3DS2Valid; } break; case JPCardDetailsModeDefault: @@ -447,26 +457,51 @@ - (void)updateSecureCodeViewModelForInput:(NSString *)input showError:(BOOL)show - (void)updateCountryViewModelForInput:(NSString *)input showError:(BOOL)showError { JPCountry *country = [JPCountry forCountryName:input]; NSString *postcodeValidationCountryName; - if ([country.alpha2Code isEqualToString:@"US"]) { + if ([country.alpha2Code isEqualToString:kAlpha2CodeUSA]) { + self.transactionViewModel.pickerStates = JPStateList.usStateList.states; + self.transactionViewModel.statePickerViewModel.placeholder = @"card_holder_state_hint"._jp_localized; + self.isStateNameValid = NO; postcodeValidationCountryName = @"country_usa"._jp_localized; - } else if ([country.alpha2Code isEqualToString:@"GB"]) { + } else if ([country.alpha2Code isEqualToString:kAlpha2CodeUK]) { postcodeValidationCountryName = @"country_uk"._jp_localized; - } else if ([country.alpha2Code isEqualToString:@"CA"]) { + self.isStateNameValid = YES; + } else if ([country.alpha2Code isEqualToString:kAlpha2CodeCanada]) { + self.transactionViewModel.pickerStates = JPStateList.caStateList.states; + self.transactionViewModel.statePickerViewModel.placeholder = @"card_holder_province_hint"._jp_localized; + self.isStateNameValid = NO; postcodeValidationCountryName = @"country_canada"._jp_localized; } else { postcodeValidationCountryName = @"country_other"._jp_localized; + self.isStateNameValid = YES; } JPValidationResult *result = [self.interactor validateCountryInput:postcodeValidationCountryName]; self.isCountryNameValid = result.isValid; self.transactionViewModel.countryPickerViewModel.errorText = (showError && input.length > 0) ? result.errorMessage : nil; self.transactionViewModel.cardholderPhoneCodeViewModel.text = [[JPCountry dialCodeForCountry:input] stringValue]; + self.transactionViewModel.statePickerViewModel.text = @""; [self.view updateViewWithViewModel:self.transactionViewModel shouldUpdateTargets:NO]; if (result.isInputAllowed) { self.transactionViewModel.countryPickerViewModel.text = country.name; } } +- (void)updateStateViewModelForInput:(NSString *)input showError:(BOOL)showError { + NSString *countryCode = [JPCountry forCountryName:self.transactionViewModel.countryPickerViewModel.text].alpha2Code; + if (!countryCode) { + return; + } + JPState *state = [JPState forStateName:input andCountryCode:countryCode]; + + JPValidationResult *result = [self.interactor validateStateInput:input]; + self.isStateNameValid = result.isValid; + self.transactionViewModel.statePickerViewModel.errorText = (showError && input.length > 0) ? result.errorMessage : nil; + [self.view updateViewWithViewModel:self.transactionViewModel shouldUpdateTargets:NO]; + if (result.isInputAllowed) { + self.transactionViewModel.statePickerViewModel.text = state.name; + } +} + - (void)updatePostalCodeViewModelForInput:(NSString *)input showError:(BOOL)showError { if (!input && self.transactionViewModel.mode == JPCardDetailsModeThreeDS2BillingDetails) { self.transactionViewModel.postalCodeInputViewModel.errorText = nil; @@ -518,12 +553,16 @@ - (JPCard *)cardFromViewModel:(JPTransactionViewModel *)viewModel { } if (viewModel.mode == JPCardDetailsModeThreeDS2BillingDetails) { + JPCountry *country = [JPCountry forCountryName:viewModel.countryPickerViewModel.text]; + NSNumber *countryCode = country ? @([country.numericCode intValue]) : nil; + NSString *state = country ? [JPState forStateName:viewModel.statePickerViewModel.text andCountryCode:country.alpha2Code].alpha2Code : nil; JPAddress *billingAddress = [[JPAddress alloc] initWithAddress1:viewModel.cardholderAddressLine1ViewModel.text address2:viewModel.cardholderAddressLine2ViewModel.text address3:viewModel.cardholderAddressLine3ViewModel.text town:viewModel.cardholderCityViewModel.text postCode:viewModel.postalCodeInputViewModel.text - countryCode:[JPCountry isoCodeForCountry:viewModel.countryPickerViewModel.text]]; + countryCode:countryCode + state:state]; card.cardAddress = billingAddress; } @@ -551,6 +590,7 @@ - (JPTransactionViewModel *)transactionViewModel { _transactionViewModel.expiryDateViewModel = [JPTransactionInputFieldViewModel viewModelWithType:JPInputTypeCardExpiryDate]; _transactionViewModel.secureCodeViewModel = [JPTransactionInputFieldViewModel viewModelWithType:JPInputTypeCardSecureCode]; _transactionViewModel.countryPickerViewModel = [JPTransactionInputFieldViewModel viewModelWithType:JPInputTypeCardCountry]; + _transactionViewModel.statePickerViewModel = [JPTransactionInputFieldViewModel viewModelWithType:JPInputTypeCardholderState]; _transactionViewModel.postalCodeInputViewModel = [JPTransactionInputFieldViewModel viewModelWithType:JPInputTypeCardPostalCode]; _transactionViewModel.addCardButtonViewModel = [JPTransactionButtonViewModel new]; _transactionViewModel.backButtonViewModel = [JPTransactionButtonViewModel new]; diff --git a/Source/Modules/Transaction/Presenter/ViewModels/JPInputType.h b/Source/Modules/Transaction/Presenter/ViewModels/JPInputType.h index 2b5ef0b29..e82be9bbc 100644 --- a/Source/Modules/Transaction/Presenter/ViewModels/JPInputType.h +++ b/Source/Modules/Transaction/Presenter/ViewModels/JPInputType.h @@ -22,10 +22,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#import - #ifndef JPInputType_h #define JPInputType_h + +#import + /** * An enum that defines the input field types present in the Add Card and Billing Details views */ @@ -36,6 +37,7 @@ typedef NS_ENUM(NSUInteger, JPInputType) { JPInputTypeCardSecureCode, JPInputTypeCardholderEmail, JPInputTypeCardCountry, + JPInputTypeCardholderState, JPInputTypeCardholderPhoneCode, JPInputTypeCardholderPhone, JPInputTypeCardholderAddressLine1, diff --git a/Source/Modules/Transaction/Presenter/ViewModels/JPTransactionViewModel.h b/Source/Modules/Transaction/Presenter/ViewModels/JPTransactionViewModel.h index 2442e3401..78c1fe816 100644 --- a/Source/Modules/Transaction/Presenter/ViewModels/JPTransactionViewModel.h +++ b/Source/Modules/Transaction/Presenter/ViewModels/JPTransactionViewModel.h @@ -116,6 +116,11 @@ typedef NS_ENUM(NSUInteger, JPCardDetailsMode); */ @property (nonatomic, strong) NSArray *pickerCountries; +/** + * An array of JPState that act as picker titles + */ +@property (nonatomic, strong) NSArray *pickerStates; + /** * The JPTransactionInputFieldViewModel for the card number input field */ @@ -146,6 +151,11 @@ typedef NS_ENUM(NSUInteger, JPCardDetailsMode); */ @property (nonatomic, strong) JPTransactionInputFieldViewModel *countryPickerViewModel; +/** + * The JPTransactionInputFieldViewModel for the state picker + */ +@property (nonatomic, strong) JPTransactionInputFieldViewModel *statePickerViewModel; + /** * The JPTransactionInputFieldViewModel for the cardholder phone code field */ diff --git a/Source/Modules/Transaction/View/JPTransactionViewController.h b/Source/Modules/Transaction/View/JPTransactionViewController.h index 766b41940..3ed37ce82 100644 --- a/Source/Modules/Transaction/View/JPTransactionViewController.h +++ b/Source/Modules/Transaction/View/JPTransactionViewController.h @@ -92,9 +92,9 @@ @end /** - * An extension that conforms to the UIPickerViewDelegate and UIPickerViewDataSource used for the country picker + * An extension that conforms to the UIPickerViewDelegate and UIPickerViewDataSource used for the country and state pickers */ -@interface JPTransactionViewController (CountryPickerDelegate) +@interface JPTransactionViewController (CountryStatePickerDelegate) @end /** diff --git a/Source/Modules/Transaction/View/JPTransactionViewController.m b/Source/Modules/Transaction/View/JPTransactionViewController.m index 922ec5d86..e91d9ec85 100644 --- a/Source/Modules/Transaction/View/JPTransactionViewController.m +++ b/Source/Modules/Transaction/View/JPTransactionViewController.m @@ -30,6 +30,7 @@ #import "JPCountry.h" #import "JPInputType.h" #import "JPLoadingButton.h" +#import "JPState.h" #import "JPTheme.h" #import "JPTransactionButton.h" #import "JPTransactionPresenter.h" @@ -39,6 +40,7 @@ @interface JPTransactionViewController () @property (nonatomic, strong) JPCardInputView *addCardView; @property (nonatomic, strong) NSArray *countries; +@property (nonatomic, strong) NSArray *states; @end @implementation JPTransactionViewController @@ -111,9 +113,15 @@ - (void)onScanCardButtonTap { - (void)updateViewWithViewModel:(JPTransactionViewModel *)viewModel shouldUpdateTargets:(BOOL)shouldUpdateTargets { if (viewModel.mode == JPCardDetailsModeAVS || viewModel.mode == JPCardDetailsModeThreeDS2BillingDetails) { + self.addCardView.countryPickerView.tag = 1; self.addCardView.countryPickerView.delegate = self; self.addCardView.countryPickerView.dataSource = self; self.countries = viewModel.pickerCountries; + + self.addCardView.statePickerView.tag = 2; + self.addCardView.statePickerView.delegate = self; + self.addCardView.statePickerView.dataSource = self; + self.states = viewModel.pickerStates; } shouldUpdateTargets ? [self updateTargets:viewModel] : NULL; [self.addCardView configureWithViewModel:viewModel]; @@ -281,30 +289,44 @@ - (void)_jp_keyboardWillHide:(NSNotification *)notification { @end -#pragma mark - Country UIPickerView delegate +#pragma mark - Country/State UIPickerView delegate -@implementation JPTransactionViewController (CountryPickerDelegate) +@implementation JPTransactionViewController (CountryStatePickerDelegate) - (NSInteger)numberOfComponentsInPickerView:(nonnull UIPickerView *)pickerView { return 1; } - (NSInteger)pickerView:(nonnull UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { - return self.countries.count; + if (pickerView.tag == 1) { + return self.countries.count; + } else { + return self.states.count; + } } - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { - JPCountry *country = self.countries[row]; - [self.presenter handleInputChange:country.name forType:JPInputTypeCardCountry showError:YES]; + if (pickerView.tag == 1) { + JPCountry *country = self.countries[row]; + [self.presenter handleInputChange:country.name forType:JPInputTypeCardCountry showError:YES]; + } else { + JPState *state = self.states[row]; + [self.presenter handleInputChange:state.name forType:JPInputTypeCardholderState showError:YES]; + } } - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { - JPCountry *country = self.countries[row]; - return country.name; + if (pickerView.tag == 1) { + JPCountry *country = self.countries[row]; + return country.name; + } else { + JPState *state = self.states[row]; + return state.name; + } } @end diff --git a/Source/Services/CardValidation/JPCardValidationService.h b/Source/Services/CardValidation/JPCardValidationService.h index c61f0449a..eba63b6b3 100644 --- a/Source/Services/CardValidation/JPCardValidationService.h +++ b/Source/Services/CardValidation/JPCardValidationService.h @@ -116,6 +116,15 @@ */ - (JPValidationResult *)validateCountryInput:(NSString *)input; +/** + * A method for validating the state + * + * @param input - an input string + * + * @return an instance of JPValidationResult that contains the validation status + */ +- (JPValidationResult *)validateStateInput:(NSString *)input; + /** * A method for validating the postal code * diff --git a/Source/Services/CardValidation/JPCardValidationService.m b/Source/Services/CardValidation/JPCardValidationService.m index 40e5c9794..8f3bc7672 100644 --- a/Source/Services/CardValidation/JPCardValidationService.m +++ b/Source/Services/CardValidation/JPCardValidationService.m @@ -27,6 +27,7 @@ #import "JPCardNetwork.h" #import "JPConstants.h" #import "JPError+Additions.h" +#import "JPState.h" #import "JPValidationResult.h" #import "NSString+Additions.h" @@ -210,6 +211,28 @@ - (JPValidationResult *)validateCountryInput:(NSString *)input { formattedInput:input]; } +- (JPValidationResult *)validateStateInput:(NSString *)input { + if ([self.selectedCountry isEqualToString:@"country_usa"._jp_localized]) { + BOOL isValid = [JPState forStateName:input andCountryCode:kAlpha2CodeUSA]; + NSString *errorMessage = isValid ? nil : @"invalid_state_should_not_be_empty"._jp_localized; + return [JPValidationResult validationWithResult:isValid + inputAllowed:YES + errorMessage:errorMessage + formattedInput:input]; + } else if ([self.selectedCountry isEqualToString:@"country_canada"._jp_localized]) { + BOOL isValid = [JPState forStateName:input andCountryCode:kAlpha2CodeCanada]; + NSString *errorMessage = isValid ? nil : @"invalid_province_territory_should_not_be_empty"._jp_localized; + return [JPValidationResult validationWithResult:isValid + inputAllowed:YES + errorMessage:errorMessage + formattedInput:input]; + } + return [JPValidationResult validationWithResult:YES + inputAllowed:NO + errorMessage:nil + formattedInput:input]; +} + - (JPValidationResult *)validatePostalCodeInput:(NSString *)input { if ([self.selectedCountry isEqualToString:@"country_usa"._jp_localized]) { return [self validatePostalCodeInput:input country:JPBillingCountryUSA]; @@ -435,7 +458,6 @@ - (JPValidationResult *)validateOtherPostalCodeInput:(NSString *)input { inputAllowed:input.length <= kOtherPostalCodeLength errorMessage:nil formattedInput:input.uppercaseString]; - ; } - (NSString *)selectedCountry { diff --git a/Source/View/CardInputView/JPCardInputView.h b/Source/View/CardInputView/JPCardInputView.h index e033758c0..ddbb29f44 100644 --- a/Source/View/CardInputView/JPCardInputView.h +++ b/Source/View/CardInputView/JPCardInputView.h @@ -111,10 +111,20 @@ @property (nonatomic, strong) JPCardInputField *_Nullable countryTextField; /** - * The picker view associated to the country input field; + * The picker view associated with the country input field */ @property (nonatomic, strong) UIPickerView *_Nullable countryPickerView; +/** + * The input field for selecting the state + */ +@property (nonatomic, strong) JPCardInputField *_Nullable stateTextField; + +/** + * The picker view associated with the state input field + */ +@property (nonatomic, strong) UIPickerView *_Nullable statePickerView; + /** * The input field for adding the postal code */ diff --git a/Source/View/CardInputView/JPCardInputView.m b/Source/View/CardInputView/JPCardInputView.m index 059292fad..882b51424 100644 --- a/Source/View/CardInputView/JPCardInputView.m +++ b/Source/View/CardInputView/JPCardInputView.m @@ -25,6 +25,7 @@ #import "JPCardDetailsMode.h" #import "JPCardInputField.h" #import "JPCardNumberField.h" +#import "JPCountry.h" #import "JPLoadingButton.h" #import "JPRoundedCornerView.h" #import "JPTheme.h" @@ -34,6 +35,7 @@ #import "UIImage+Additions.h" #import "UIStackView+Additions.h" #import "UIView+Additions.h" +#import "JPConstants.h" @interface JPCardInputView () @@ -174,6 +176,7 @@ - (void)applyTheme:(JPTheme *)theme { [self.cardExpiryTextField applyTheme:theme]; [self.secureCodeTextField applyTheme:theme]; [self.countryTextField applyTheme:theme]; + [self.stateTextField applyTheme:theme]; [self.postcodeTextField applyTheme:theme]; } @@ -268,10 +271,13 @@ - (void)configureWithViewModel:(JPTransactionViewModel *)viewModel { [self.cardHolderAddressLine3TextField configureWithViewModel:viewModel.cardholderAddressLine3ViewModel]; [self.cardHolderPhoneCodeTextField configureWithViewModel:viewModel.cardholderPhoneCodeViewModel]; [self.countryTextField configureWithViewModel:viewModel.countryPickerViewModel]; + [self.stateTextField configureWithViewModel:viewModel.statePickerViewModel]; [self.postcodeTextField configureWithViewModel:viewModel.postalCodeInputViewModel]; break; } [_countryPickerView reloadAllComponents]; + [_statePickerView reloadAllComponents]; + [self updateStatePicker:viewModel.countryPickerViewModel.text]; [self.addCardButton configureWithViewModel:viewModel.addCardButtonViewModel]; [self.backButton configureWithViewModel:viewModel.backButtonViewModel]; } @@ -290,10 +296,20 @@ - (void)enableUserInterface:(BOOL)shouldEnable { self.cardExpiryTextField.enabled = shouldEnable; self.secureCodeTextField.enabled = shouldEnable; self.countryTextField.enabled = shouldEnable; + self.stateTextField.enabled = shouldEnable; self.postcodeTextField.enabled = shouldEnable; self.addCardButton.enabled = shouldEnable; } +- (void)updateStatePicker:(NSString *)countryName { + NSString *countryCode = [JPCountry forCountryName:countryName].alpha2Code; + if (!countryCode) { + return; + } + BOOL showStateField = [countryCode isEqualToString:kAlpha2CodeUSA] || [countryCode isEqualToString:kAlpha2CodeCanada]; + self.stateTextField.hidden = !showStateField; +} + #pragma mark - Layout setup - (void)setupConstraints { @@ -341,6 +357,7 @@ - (void)setupContentsConstraints { [self.cardExpiryTextField.heightAnchor constraintEqualToConstant:kInputFieldHeight], [self.secureCodeTextField.heightAnchor constraintEqualToConstant:kInputFieldHeight], [self.countryTextField.heightAnchor constraintEqualToConstant:kInputFieldHeight], + [self.stateTextField.heightAnchor constraintEqualToConstant:kInputFieldHeight], [self.postcodeTextField.heightAnchor constraintEqualToConstant:kInputFieldHeight], [self.backButton.heightAnchor constraintEqualToConstant:kAddCardButtonHeight], [self.addCardButton.heightAnchor constraintEqualToConstant:kAddCardButtonHeight], @@ -530,6 +547,23 @@ - (UIPickerView *)countryPickerView { return _countryPickerView; } +- (JPCardInputField *)stateTextField { + if (!_stateTextField) { + _stateTextField = [JPCardInputField new]; + _stateTextField.inputView = self.statePickerView; + _stateTextField.accessibilityIdentifier = @"State Field"; + } + return _stateTextField; +} + +- (UIPickerView *)statePickerView { + if (!_statePickerView) { + _statePickerView = [UIPickerView new]; + _statePickerView.accessibilityIdentifier = @"State Picker"; + } + return _statePickerView; +} + - (JPCardInputField *)postcodeTextField { if (!_postcodeTextField) { _postcodeTextField = [JPCardInputField new]; @@ -605,6 +639,7 @@ - (UIStackView *)billingDetails { [stackView addArrangedSubview:self.cardHolderEmailTextField]; if (_mode != JPCardDetailsModeAVS) { [stackView addArrangedSubview:self.countryTextField]; + [stackView addArrangedSubview:self.stateTextField]; } [stackView addArrangedSubview:self.phoneStackView]; [stackView addArrangedSubview:self.cardHolderAddressLine1TextField]; diff --git a/Source/include/JPState.h b/Source/include/JPState.h new file mode 120000 index 000000000..4c5d69543 --- /dev/null +++ b/Source/include/JPState.h @@ -0,0 +1 @@ +../Models/State/JPState.h \ No newline at end of file diff --git a/Source/include/create_symlinks.sh b/Source/include/create_symlinks.sh index 93a3f68e4..4a65a8f42 100755 --- a/Source/include/create_symlinks.sh +++ b/Source/include/create_symlinks.sh @@ -37,6 +37,7 @@ ln -s -f ../View/PBBAButton/JPPBBAButton.h JPPBBAButton.h ln -s -f ../Models/Theme/JPThemable.h JPThemable.h ln -s -f ../Models/UIConfiguration/JPUIConfiguration.h JPUIConfiguration.h ln -s -f ../Models/Country/JPCountry.h JPCountry.h +ln -s -f ../Models/State/JPState.h JPState.h ln -s -f ../Models/PaymentMethod/JPPaymentMethodType.h JPPaymentMethodType.h ln -s -f ../Models/Reference/JPReference.h JPReference.h ln -s -f ../Models/Authorization/Session/JPSessionAuthorization.h JPSessionAuthorization.h diff --git a/scripts/test.sh b/scripts/test.sh index 9542cdc2f..f8339e9e9 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -5,4 +5,4 @@ set -e xcodebuild clean test \ -project JudoKit_iOS.xcodeproj \ -scheme JudoKit_iOS \ - -destination 'platform=iOS Simulator,name=iPhone 13' + -destination 'platform=iOS Simulator,name=iPhone 14'