diff --git a/JudoKitObjC.podspec b/JudoKitObjC.podspec index 6497fdac..2a7a014e 100755 --- a/JudoKitObjC.podspec +++ b/JudoKitObjC.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'JudoKitObjC' - s.version = '8.2.0' + s.version = '8.2.1' s.summary = 'Judo Pay Full iOS Client Kit' s.homepage = 'https://www.judopay.com/' s.license = 'MIT' diff --git a/Source/API/JPCheckCard.h b/Source/API/JPCheckCard.h index 69b85308..1822d64f 100644 --- a/Source/API/JPCheckCard.h +++ b/Source/API/JPCheckCard.h @@ -22,8 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#import #import "JPTransaction.h" +#import /** * A JPCheckCard object is returned from invoking the Judo SDK 'checkCard' method, and contains the necessary details of the transaction diff --git a/Source/API/JPTransaction.m b/Source/API/JPTransaction.m index c40dfa76..b5f70b28 100644 --- a/Source/API/JPTransaction.m +++ b/Source/API/JPTransaction.m @@ -102,10 +102,7 @@ - (NSError *)validateTransaction { //!OCLINT return [NSError judoReferenceMissingError]; } - if (![self isKindOfClass:JPRegisterCard.class] - && ![self isKindOfClass:JPCheckCard.class] - && ![self isKindOfClass:JPSaveCard.class] - && !self.amount) { + if (![self isKindOfClass:JPRegisterCard.class] && ![self isKindOfClass:JPCheckCard.class] && ![self isKindOfClass:JPSaveCard.class] && !self.amount) { return [NSError judoAmountMissingError]; } diff --git a/Source/Controller/JudoPayViewController.m b/Source/Controller/JudoPayViewController.m index 88b3a699..525f2346 100644 --- a/Source/Controller/JudoPayViewController.m +++ b/Source/Controller/JudoPayViewController.m @@ -340,7 +340,7 @@ - (void)setupView { // Layout Constraints self.keyboardHeightConstraint = [self.paymentButton.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:0]; self.paymentButtonHeightConstraint = [self.paymentButton.heightAnchor constraintEqualToConstant:self.theme.buttonHeight]; - + NSArray *constraints = @[ self.paymentButtonHeightConstraint, [self.paymentButton.leftAnchor constraintEqualToAnchor:self.view.safeLeftAnchor], @@ -471,7 +471,7 @@ - (void)setupView { - (void)viewSafeAreaInsetsDidChange { [super viewSafeAreaInsetsDidChange]; - + CGFloat bottomInset = 0.0; if (@available(iOS 11.0, *)) { @@ -479,7 +479,7 @@ - (void)viewSafeAreaInsetsDidChange { } else { bottomInset = self.bottomLayoutGuide.length; } - + [self.paymentButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, bottomInset, 0)]; self.paymentButtonHeightConstraint.constant = self.theme.buttonHeight + bottomInset; [self.paymentButton setNeedsUpdateConstraints]; @@ -876,17 +876,30 @@ - (void)judoPayInputDidChangeText:(JPInputField *)input { #pragma mark - WKNavigation Delegate Methods -- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { +- (void)handleRedirectForWebView:(WKWebView *)webView + redirectURL:(NSString *)redirectURL + decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - NSString *urlString = navigationAction.request.URL.absoluteString; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + NSMutableString *javascriptCode = [NSMutableString new]; - if ([urlString rangeOfString:@"Parse3DS"].location == NSNotFound) { - decisionHandler(WKNavigationActionPolicyAllow); - return; - } + [javascriptCode appendString:@"const paRes = document.getElementsByName('PaRes')[0].value;"]; + [javascriptCode appendString:@"const md = document.getElementsByName('MD')[0].value;"]; + [javascriptCode appendString:@"[paRes, md]"]; - NSString *bodyString = [[NSString alloc] initWithData:navigationAction.request.HTTPBody encoding:NSUTF8StringEncoding]; - if (!bodyString) { + [webView evaluateJavaScript:javascriptCode + completionHandler:^(NSArray *response, NSError *error) { + NSDictionary *responseDictionary = [self mapToDictionaryWithResponse:response]; + [self setLoadingViewTitleForTransactionType:self.transactionType]; + [self handleACSFormWithResponse:responseDictionary decisionHandler:decisionHandler]; + }]; + }); +} + +- (void)handleACSFormWithResponse:(NSDictionary *)response + decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + + if (!self.pending3DSReceiptId) { if (self.completionBlock) { self.completionBlock(nil, [NSError judo3DSRequestFailedErrorWithUnderlyingError:nil]); } @@ -894,48 +907,71 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigati return; } - NSDictionary *results = [bodyString extractURLComponentsQueryItems]; + [self.loadingView startAnimating]; + self.title = self.theme.authenticationTitle; + + [self.transaction threeDSecureWithParameters:response + receiptId:self.pending3DSReceiptId + completion:^(JPResponse *response, NSError *error) { + [self.loadingView stopAnimating]; + + if (self.completionBlock) { + if (response) { + self.completionBlock(response, nil); + decisionHandler(WKNavigationActionPolicyAllow); + + } else { + NSError *judoError = error ? error : NSError.judoResponseParseError; + self.completionBlock(nil, judoError); + decisionHandler(WKNavigationActionPolicyCancel); + } + } + }]; +} - if (self.pending3DSReceiptId) { - if (self.transactionType == TransactionTypeRegisterCard) { - self.loadingView.actionLabel.text = self.theme.verifying3DSRegisterCardTitle; - } else { - self.loadingView.actionLabel.text = self.theme.verifying3DSPaymentTitle; - } +- (NSDictionary *)mapToDictionaryWithResponse:(NSArray *)response { - [self.loadingView startAnimating]; - self.title = self.theme.authenticationTitle; - [self.transaction threeDSecureWithParameters:results - receiptId:self.pending3DSReceiptId - completion:^(JPResponse *response, NSError *error) { - [self.loadingView stopAnimating]; - if (self.completionBlock) { - if (response) { - self.completionBlock(response, nil); - } else { - NSError *judoError = error ? error : [NSError judo3DSRequestFailedErrorWithUnderlyingError:nil]; - self.completionBlock(nil, judoError); - } - } - }]; + if (response.count != 2) + return nil; + + return @{ + @"PaRes" : response[0], + @"MD" : [response[1] stringByReplacingOccurrencesOfString:@" " withString:@"+"] + }; +} + +- (void)setLoadingViewTitleForTransactionType:(TransactionType)transactionType { + if (transactionType == TransactionTypeRegisterCard) { + self.loadingView.actionLabel.text = self.theme.verifying3DSRegisterCardTitle; } else { - if (self.completionBlock) { - self.completionBlock(nil, [NSError judo3DSRequestFailedErrorWithUnderlyingError:nil]); - } + self.loadingView.actionLabel.text = self.theme.verifying3DSPaymentTitle; } +} - [UIView animateWithDuration:0.3 - animations:^{ self.threeDSWebView.alpha = 0.0f; } - completion:^(BOOL finished) { - [self.threeDSWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]]; - }]; +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - decisionHandler(WKNavigationActionPolicyCancel); + NSString *urlString = navigationAction.request.URL.absoluteString; + + if ([urlString rangeOfString:@"Parse3DS"].location == NSNotFound) { + decisionHandler(WKNavigationActionPolicyAllow); + return; + } + + [self handleRedirectForWebView:webView redirectURL:urlString decisionHandler:decisionHandler]; return; } - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { + NSMutableString *scriptContent = [NSMutableString stringWithString:@"const meta = document.createElement('meta');"]; + [scriptContent appendString:@"meta.name='viewport';"]; + [scriptContent appendString:@"meta.content='width=device-width';"]; + [scriptContent appendString:@"const head = document.getElementsByTagName('head')[0];"]; + [scriptContent appendString:@"head.appendChild(meta);"]; + [scriptContent appendString:@"meta.name"]; + + [_threeDSWebView evaluateJavaScript:scriptContent completionHandler:nil]; + CGFloat alphaVal = 1.0f; if ([webView.URL.absoluteString isEqualToString:@"about:blank"]) { alphaVal = 0.0f; diff --git a/Source/Controller/JudoPaymentMethodsViewController.m b/Source/Controller/JudoPaymentMethodsViewController.m index 5aee72de..63e19a6e 100644 --- a/Source/Controller/JudoPaymentMethodsViewController.m +++ b/Source/Controller/JudoPaymentMethodsViewController.m @@ -187,7 +187,7 @@ - (void)onCardPaymentButtonDidTap { currentSession:self.judoKitSession cardDetails:self.viewModel.cardDetails completion:completion]; - + viewController.primaryAccountDetails = self.viewModel.primaryAccountDetails; viewController.theme = self.theme; [self.navigationController pushViewController:viewController animated:YES]; diff --git a/Source/Extensions/NSError+Judo.m b/Source/Extensions/NSError+Judo.m index 60b6d936..ccd44337 100644 --- a/Source/Extensions/NSError+Judo.m +++ b/Source/Extensions/NSError+Judo.m @@ -163,9 +163,9 @@ + (NSError *)judoParameterError { } + (NSError *)judoApplePayConfigurationError { - return [NSError errorWithDomain:JudoErrorDomain - code:JudoErrorInvalidApplePayConfiguration - userInfo:@{NSLocalizedDescriptionKey : @"Invalid Apple Pay configuration"}]; + return [NSError errorWithDomain:JudoErrorDomain + code:JudoErrorInvalidApplePayConfiguration + userInfo:@{NSLocalizedDescriptionKey : @"Invalid Apple Pay configuration"}]; } + (NSError *)judoInvalidCardNumberError { diff --git a/Source/Info.plist b/Source/Info.plist index bb3e4ccf..63dfe5b9 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 8.2.0 + 8.2.1 CFBundleSignature ???? CFBundleVersion diff --git a/Source/JudoKit.h b/Source/JudoKit.h index 6aecd4a8..cf827a62 100644 --- a/Source/JudoKit.h +++ b/Source/JudoKit.h @@ -30,7 +30,7 @@ #import "JudoPaymentMethodsViewController.h" #import "PaymentMethods.h" -static NSString *__nonnull const JudoKitVersion = @"8.2.0"; +static NSString *__nonnull const JudoKitVersion = @"8.2.1"; @class JudoPayViewController; @class ApplePayConfiguration; diff --git a/Source/JudoKit.m b/Source/JudoKit.m index bfb3ba01..f9c67ad3 100644 --- a/Source/JudoKit.m +++ b/Source/JudoKit.m @@ -31,8 +31,8 @@ #import "CardInputField.h" #import "DateInputField.h" #import "FloatingTextField.h" -#import "JPCollection.h" #import "JPCheckCard.h" +#import "JPCollection.h" #import "JPInputField.h" #import "JPPayment.h" #import "JPPreAuth.h" @@ -84,7 +84,7 @@ - (instancetype)initWithToken:(NSString *)token secret:(NSString *)secret { - (instancetype)initWithToken:(NSString *)token secret:(NSString *)secret allowJailbrokenDevices:(BOOL)jailbrokenDevicesAllowed { - + self = [super init]; if (!self) @@ -110,7 +110,7 @@ - (instancetype)initWithToken:(NSString *)token - (void)sendWithCompletion:(nonnull JPTransaction *)transaction completion:(nonnull JudoCompletionBlock)completion { - + [transaction sendWithCompletion:completion]; } @@ -129,8 +129,7 @@ - (void)presentPaymentViewControllerWithJudoId:(NSString *)judoId currentSession:self cardDetails:cardDetails completion:completion]; - - + viewController.primaryAccountDetails = self.primaryAccountDetails; viewController.paymentToken = paymentToken; viewController.theme = self.theme; @@ -145,7 +144,7 @@ - (JPTransaction *)transactionForTypeClass:(Class)type judoId:(NSString *)judoId amount:(nullable JPAmount *)amount reference:(nonnull JPReference *)reference { - + JPTransaction *transaction = [type new]; transaction.judoId = judoId; transaction.amount = amount; @@ -161,7 +160,7 @@ - (JPTransaction *)transactionForType:(TransactionType)type judoId:(NSString *)judoId amount:(JPAmount *)amount reference:(JPReference *)reference { - + Class transactionTypeClass; switch (type) { @@ -180,7 +179,7 @@ - (JPTransaction *)transactionForType:(TransactionType)type case TransactionTypeSaveCard: transactionTypeClass = JPSaveCard.class; break; - + case TransactionTypeCheckCard: transactionTypeClass = JPCheckCard.class; break; @@ -198,7 +197,7 @@ - (JPTransaction *)transactionForType:(TransactionType)type - (JPPayment *)paymentWithJudoId:(NSString *)judoId amount:(JPAmount *)amount reference:(JPReference *)reference { - + return (JPPayment *)[self transactionForTypeClass:JPPayment.class judoId:judoId amount:amount @@ -208,7 +207,7 @@ - (JPPayment *)paymentWithJudoId:(NSString *)judoId - (JPPreAuth *)preAuthWithJudoId:(NSString *)judoId amount:(JPAmount *)amount reference:(JPReference *)reference { - + return (JPPreAuth *)[self transactionForTypeClass:JPPreAuth.class judoId:judoId amount:amount @@ -217,7 +216,7 @@ - (JPPreAuth *)preAuthWithJudoId:(NSString *)judoId - (JPRegisterCard *)registerCardWithJudoId:(NSString *)judoId reference:(JPReference *)reference { - + return (JPRegisterCard *)[self transactionForTypeClass:JPRegisterCard.class judoId:judoId amount:nil @@ -227,7 +226,7 @@ - (JPRegisterCard *)registerCardWithJudoId:(NSString *)judoId - (JPCheckCard *)checkCardWithJudoId:(NSString *)judoId currency:(NSString *)currency reference:(JPReference *)reference { - + return (JPCheckCard *)[self transactionForTypeClass:JPRegisterCard.class judoId:judoId amount:currency ? [JPAmount amount:@"0.0" currency:currency] : nil @@ -245,17 +244,17 @@ - (JPSaveCard *)saveCardWithJudoId:(NSString *)judoId - (JPTransactionProcess *)transactionProcessForType:(Class)type receiptId:(NSString *)receiptId amount:(JPAmount *)amount { - + JPTransactionProcess *transactionProc = [[type alloc] initWithReceiptId:receiptId amount:amount]; - + transactionProc.apiSession = self.apiSession; return transactionProc; } - (JPCollection *)collectionWithReceiptId:(NSString *)receiptId amount:(JPAmount *)amount { - + return (JPCollection *)[self transactionProcessForType:JPCollection.class receiptId:receiptId amount:amount]; @@ -263,7 +262,7 @@ - (JPCollection *)collectionWithReceiptId:(NSString *)receiptId - (JPVoid *)voidWithReceiptId:(NSString *)receiptId amount:(JPAmount *)amount { - + return (JPVoid *)[self transactionProcessForType:JPVoid.class receiptId:receiptId amount:amount]; @@ -271,7 +270,7 @@ - (JPVoid *)voidWithReceiptId:(NSString *)receiptId - (JPRefund *)refundWithReceiptId:(NSString *)receiptId amount:(JPAmount *)amount { - + return (JPRefund *)[self transactionProcessForType:JPRefund.class receiptId:receiptId amount:amount]; @@ -284,9 +283,9 @@ - (JPReceipt *)receipt:(NSString *)receiptId { } - (void)list:(Class)type - paginated:(JPPagination *)pagination - completion:(JudoCompletionBlock)completion { - + paginated:(JPPagination *)pagination + completion:(JudoCompletionBlock)completion { + JPTransaction *transaction = [type new]; transaction.apiSession = self.apiSession; [transaction listWithPagination:pagination completion:completion]; @@ -357,7 +356,7 @@ - (void)invokePayment:(NSString *)judoId consumerReference:(NSString *)reference cardDetails:(JPCardDetails *)cardDetails completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:amount reference:[[JPReference alloc] initWithConsumerReference:reference] @@ -372,7 +371,7 @@ - (void)invokePayment:(NSString *)judoId reference:(JPReference *)reference cardDetails:(JPCardDetails *)cardDetails completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:amount reference:reference @@ -387,7 +386,7 @@ - (void)invokePreAuth:(NSString *)judoId consumerReference:(NSString *)reference cardDetails:(JPCardDetails *)cardDetails completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:amount reference:[[JPReference alloc] initWithConsumerReference:reference] @@ -401,7 +400,7 @@ - (void)invokeRegisterCard:(NSString *)judoId consumerReference:(NSString *)reference cardDetails:(JPCardDetails *)cardDetails completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:nil reference:[[JPReference alloc] initWithConsumerReference:reference] @@ -415,7 +414,7 @@ - (void)invokeCheckCard:(NSString *)judoId currency:(NSString *)currency reference:(JPReference *)reference cardDetails:(JPCardDetails *)cardDetails - completion:(void (^)(JPResponse * _Nullable, NSError * _Nullable))completion { + completion:(void (^)(JPResponse *_Nullable, NSError *_Nullable))completion { [self presentPaymentViewControllerWithJudoId:judoId amount:currency ? [JPAmount amount:@"0.0" currency:currency] : nil reference:reference @@ -429,7 +428,7 @@ - (void)invokeSaveCard:(NSString *)judoId consumerReference:(NSString *)reference cardDetails:(JPCardDetails *)cardDetails completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:nil reference:[[JPReference alloc] initWithConsumerReference:reference] @@ -445,7 +444,7 @@ - (void)invokeTokenPayment:(NSString *)judoId cardDetails:(JPCardDetails *)cardDetails paymentToken:(JPPaymentToken *)paymentToken completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:amount reference:[[JPReference alloc] initWithConsumerReference:reference] @@ -461,7 +460,7 @@ - (void)invokeTokenPreAuth:(NSString *)judoId cardDetails:(JPCardDetails *)cardDetails paymentToken:(JPPaymentToken *)paymentToken completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:amount reference:[[JPReference alloc] initWithConsumerReference:reference] @@ -476,7 +475,7 @@ - (void)invokePreAuth:(NSString *)judoId reference:(JPReference *)reference cardDetails:(JPCardDetails *)cardDetails completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:amount reference:reference @@ -490,7 +489,7 @@ - (void)invokeRegisterCard:(NSString *)judoId reference:(JPReference *)reference cardDetails:(JPCardDetails *)cardDetails completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:nil reference:reference @@ -504,7 +503,7 @@ - (void)invokeSaveCard:(NSString *)judoId reference:(JPReference *)reference cardDetails:(JPCardDetails *)cardDetails completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:nil reference:reference @@ -520,7 +519,7 @@ - (void)invokeTokenPayment:(NSString *)judoId cardDetails:(JPCardDetails *)cardDetails paymentToken:(JPPaymentToken *)paymentToken completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:amount reference:reference @@ -536,7 +535,7 @@ - (void)invokeTokenPreAuth:(NSString *)judoId cardDetails:(JPCardDetails *)cardDetails paymentToken:(JPPaymentToken *)paymentToken completion:(void (^)(JPResponse *, NSError *))completion { - + [self presentPaymentViewControllerWithJudoId:judoId amount:amount reference:reference @@ -564,7 +563,7 @@ - (void)invokeApplePayWithConfiguration:(ApplePayConfiguration *)configuration completion(nil, NSError.judoApplePayConfigurationError); return; } - + self.viewController.delegate = self; self.completionBlock = completion; diff --git a/Source/Model/JPPrimaryAccountDetails.m b/Source/Model/JPPrimaryAccountDetails.m index bf6d7d43..694453c6 100644 --- a/Source/Model/JPPrimaryAccountDetails.m +++ b/Source/Model/JPPrimaryAccountDetails.m @@ -31,36 +31,36 @@ + (instancetype)detailsFromDictionary:(NSDictionary *)dictionary { } - (instancetype)initFromDictionary:(NSDictionary *)dictionary { - + if (self = [super init]) { _name = dictionary[@"name"]; _accountNumber = dictionary[@"accountNumber"]; _dateOfBirth = dictionary[@"dateOfBirth"]; _postCode = dictionary[@"postCode"]; } - + return self; } - (NSDictionary *)toDictionary { NSMutableDictionary *dictionary = [NSMutableDictionary new]; - + if (_name) { dictionary[@"name"] = _name; } - + if (_accountNumber) { dictionary[@"accountNumber"] = _accountNumber; } - + if (_dateOfBirth) { dictionary[@"dateOfBirth"] = _dateOfBirth; } - + if (_postCode) { dictionary[@"postCode"] = _postCode; } - + return dictionary; } diff --git a/Source/View/JP3DSWebView.m b/Source/View/JP3DSWebView.m index 0470eb77..a5a21991 100644 --- a/Source/View/JP3DSWebView.m +++ b/Source/View/JP3DSWebView.m @@ -69,6 +69,7 @@ - (NSString *)load3DSWithPayload:(NSDictionary *)payload error:(NSError **)error NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; request.HTTPMethod = @"POST"; + [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; [request setValue:[NSString stringWithFormat:@"%li", (unsigned long)postData.length] forHTTPHeaderField:@"Content-Length"]; request.HTTPBody = postData; diff --git a/Source/ViewModel/JudoPaymentMethodsViewModel.m b/Source/ViewModel/JudoPaymentMethodsViewModel.m index 32ee29ed..6517e4d2 100644 --- a/Source/ViewModel/JudoPaymentMethodsViewModel.m +++ b/Source/ViewModel/JudoPaymentMethodsViewModel.m @@ -33,7 +33,7 @@ - (instancetype)initWithJudoId:(nonnull NSString *)judoId primaryAccountDetails:(JPPrimaryAccountDetails *)primaryAccountDetails applePayConfiguration:(nullable ApplePayConfiguration *)applePayConfiguration cardDetails:(nullable JPCardDetails *)cardDetails { - + if (self = [super init]) { _judoId = judoId; _amount = amount;