2
votes

I am implementing a secure payment solution in Native iOS App in Swift.

Basically when i start a payment request , in response i get a "PaReq" and "ACSURL" in the response, if the payment card is 3D Check enrolled.

Then i need to do java script POST.

And once the user completed action , i need to extract "PaRes" and send it in next service call.

Here is the working solution in Objective-C using UIWebView.I want to achieve same thing in Swift Using WKWebView as UIWebView is deprecated.

- (void)viewDidLoad {
    [super viewDidLoad];

    UIWebView *webView=[[UIWebView alloc]initWithFrame:CGRectMake(0, 0, self.secureViewContainer.frame.size.width ,self.secureViewContainer.frame.size.height)];
    webView.delegate = self;
    [self.view addSubview:webView];

    NSString *termURL = @"https://www.apple.com";

    self.webViewURL = @"";
    self.webViewHTMLString = [NSString stringWithFormat:@"<html><body><form name='redirectToIssuerForm' id='redirectToIssuerForm' action='%@' method='post'><input type='hidden' name='PaReq' value='%@' /><input type='hidden' name='TermUrl' value='%@' /><input type='hidden' name='MD' value='' /><input type='submit' id='submitButton' value='Click here to continue' /></form><script>function submitForm(){document.getElementById('submitButton').style.display='none'; document.getElementById('submitButton').click();} window.onload = (function(){submitForm();});</script></body></html>",self.acsurl,self.pareq,termURL];

    [webView loadHTMLString:self.webViewHTMLString baseURL:nil];

}

- (void)webViewDidFinishLoad:(UIWebView *)webView{

    if([webView.request.URL.absoluteString isEqualToString:@"about:blank"]){
        [webView stringByEvaluatingJavaScriptFromString:@"submitForm()"];
    }
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{

    if([ [NSString stringWithFormat:@"%@",request.URL] isEqualToString:@"https://www.apple.com/"]){ 

    NSData *data = request.HTTPBody;

    NSString *dataString = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];

    NSLog(@"\n\n\n  3D Secure - dataString: %@", dataString);

        return NO;
    }
    else{

        return YES;

    }
}

Here is what i have tried to migrate to Swift Using WKWebView :

override func viewDidLoad() {

    super.viewDidLoad()

    let webConfiguration = WKWebViewConfiguration()
    let customFrame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: 0.0, height: self.secureViewContainer.frame.size.height))
    self.webView = WKWebView (frame: customFrame , configuration: webConfiguration)
    webView.uiDelegate = self
    webView.navigationDelegate = self
    self.secureViewContainer.addSubview(webView)          
}
override func viewWillAppear(_ animated: Bool) {

    super.viewWillAppear(animated)
    let termURLString = "https://www.apple.com"
    var paReqString = response.PaRequest
    var acsurlString = response.ACSURL

    // create HTML String 

    let webViewHTMLString = String(format:"<html><body><form name='redirectToIssuerForm' id='redirectToIssuerForm' action='%@' method='post'><input type='hidden' name='PaReq' value='%@' /><input type='hidden' name='TermUrl' value='%@' /><input type='hidden' name='MD' value='' /><input type='submit' id='submitButton' value='Click here to continue' /></form><script>function submitForm(){document.getElementById('submitButton').style.display='none'; document.getElementById('submitButton').click();} window.onload = (function(){submitForm();});</script></body></html>",acsurlString,paReqString,termURLString)

        // Load HTML String
        webView.loadHTMLString(webViewHTMLString, baseURL: nil)
 }

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {

    if webView.url?.absoluteString == "about:blank" {

        webView.evaluateJavaScript("submitForm()", completionHandler: {
            result in

            print("\n\n\n Result - \(result)\n\n\n")

        })
    }

}

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

    if let url = navigationAction.request.url,url.absoluteString.hasPrefix("https://www.apple.com/") {
        let data = navigationAction.request.httpBody

        print("\n\n  data : \(String(describing: data))")

        decisionHandler(.cancel)
        return
    }
    decisionHandler(.allow)
}

The problem is both "Result" and "data" are nil.

1 : Will i get the java script result in webView(_:decidePolicyFor:decisionHandler:) (or) in webView:didFinishNavigation: ?

2 : In UIWebView working solution i am able to change termURL to any url like "https://www.apple.com" , still i am able get Data fine , Can someone explain me how this Javascript POST works ?

3 : Please point me to a guide/tutorial to understand more about JavaScript and Native iOS App interaction.

2

2 Answers

2
votes

It looks like you're on the right track. I recently wrote an article on this subject, and that should explain everything you need.

First of all, if you want to send something to the native code from your javascript code, you need to use window.webkit in your javascript code. You'll need to implement the WKScriptMessageHandler and WKNavigationDelegate in your swift code, and implement this function:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    //This function handles the events coming from javascript.
    //We can access properties through the message body, like this:
    guard let response = message.body as? String else { return }
    print(response)
}

More information on this is in the article.

All the sending you will be doing will go through webview.evaluateJavascript(). Make sure the javascript function works, because webkit errors won't show up in your Xcode console. You'll have to implement a custom messageHandler for that.

1
votes
  1. You get the result in func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!), BUT only inside the completionHandler closure, not function-wide.

  2. As I see it termURL is just the content of some hidden input field of the form. Its value is passed when submitting the form - but it has nothing to do with where it is being submitted to.

  3. This seems a good, but basic intro into WkWebViews. And there's this blog post about WkWebView JavaScript Quirks.