WKWebView配置H5支付

1,191 阅读4分钟

都是在WKWebView绑定协议,在navigation request协议里面处理

  • (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

支付宝支付

注意:

  • 需要对alipays进行替换为自己项目的Schemes,这样返回才会打开自己的app。
  • 支付宝判定是否能打开很容易失败,所以直接打开支付宝支付。

代码说明:

  • Config.PayDomain为自己配置schemes,可以是微信支付域名统一调用,也可以使用xxx.com的项目名称回调。
  • urlDecoded和urlEncoded 去解码编码来完成域名替换。

编解码代码如下,且放在String的Extension里:

    //解码:把任何参数转换成适合放在URL中的字符串。
     func urlEncoded() -> String {
         let encodeUrlString = self.addingPercentEncoding(withAllowedCharacters:
             .urlQueryAllowed)
         return encodeUrlString ?? ""
     }
  
     //将编码后的url转换回原始的url
     func urlDecoded() -> String {
         return self.removingPercentEncoding ?? ""
     }

支付代码如下:

         guard let url = navigationAction.request.url else {
            decisionHandler(.allow)
            return
        }
        let reqUrl = url.absoluteString
        // 唤起支付宝  alipays添加在info的URLtype  方便支付宝取消支付返回
        if ((reqUrl.hasPrefix("alipays://")) == true) || ((reqUrl.hasPrefix("alipay://")) == true) {
          // 解决跳转到本地支付宝App不返回的问题  解码URL 将alipays替换成功自己的URL Schemes  在taget -> info -> URL Types设置
            let aliURLString = reqUrl.urlDecoded().replacingOccurrences(of: "alipays", with: Config.jiaYiDoctorScheme)
            let openedURL = URL.init(string: aliURLString.urlEncoded())!
          // 支付宝判定是否打开很容易导致失败 所以是直接打开
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(openedURL) { (bSucc) in
                    if bSucc == false {
                      ProgressHUD.showMessage("您还未安装支付宝App,请安装后重试")
                    }
                }
                UIApplication.shared.open(openedURL,  completionHandler: nil)
            } else {
                let bSucc = UIApplication.shared.openURL(openedURL)
                if bSucc == false {
                  ProgressHUD.showMessage("您还未安装支付宝App,请安装后重试")
                }
            }
        }

微信

注意: 1.和支付宝一样需要配置自己app的schemes才会跳转,但是微信必须要将一级域名配置为schemes,否则App也会报商家参数格式错误。 2.需要对微信支付重新加载,配置Referer,值为公司在微信申请的一级域名,否则会报商家参数格式错误。 3.微信返回为H5写的加载页,再返回会有标题为weixin的空白,如需要处理空白页需要在Appdelegate里面收到微信回调发送通知,重新加载web页面处理。

Appdelegate的协议里面处理: - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {// [self wxPayHandlerPostNoti:url];  return YES;}

具体代码如下,代码说明同支付宝:

               var redirectUrl:String?
        // 微信的必须将redirect_url也配置为支付域名 否则也会报商户错误
        if ((reqUrl.hasPrefix("https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb")) == true && (reqUrl.hasSuffix("redirect_url=\(Config.PayDomain)://")) == false) {
          decisionHandler(.cancel)
          // 添加自己的app的scheme方便回调  如果不更新返回会跳转到浏览器去
            if ((reqUrl.contains("redirect_url=")) == true) {
                let array = reqUrl.components(separatedBy: "redirect_url=")
                if array.count == 2 {
                endPayRedirectURL = array[1]
                redirectUrl = array[0] + "redirect_url=\(Config.PayDomain)://"
            }
          } else {
            redirectUrl = reqUrl +  "redirect_url=\(Config.PayDomain)://"
          }
          // 设置请求信息 避免会跳转浏览器说商家信息格式错误  微信也需要重新定向加载一次
          let newRequest:NSMutableURLRequest = NSMutableURLRequest.init(url: URL.init(string: redirectUrl!)!)
          newRequest.allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields
          newRequest.setValue(Config.PayDomain, forHTTPHeaderField: "Referer")
          newRequest.url = URL.init(string: redirectUrl!)
          webView.load(newRequest as URLRequest)
          return
        }
    
        let scheme:String = navigationAction.request.url?.scheme ?? ""
        if scheme != "https" && scheme != "http" {
          decisionHandler(.cancel)
          // 加载微信页面钱要先加载自己的结果页面  但是结果页面返回会有空白页面
          //如果不展示空白页面可以在页面微信支付结果回调后发送通知刷新页面 这样不会有空白页面
          if scheme == "weixin" {
            if endPayRedirectURL != nil {
              // 必须要再次解码 否则返回为空白页面 但是在返回还是weixin标题的空白页 只有appdelega添加这个结果刷新才不会有空白页
               webView.load(URLRequest.init(url: URL.init(string: endPayRedirectURL!.urlDecoded())!))
            }
          }
          // 打开微信支付
            if reqUrl.hasPrefix("weixin://") {
                if UIApplication.shared.canOpenURL(url) == false {
                ProgressHUD.showMessage("您还未安装微信App,请安装后重试")
            } else {
                if #available(iOS 10.0, *) {
                    UIApplication.shared.open(url)
                } else {
                    UIApplication.shared.openURL(url)
                }
            }
          }
          return
        }


完整代码:

  func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        guard let url = navigationAction.request.url else {
            decisionHandler(.allow)
            return
        }
        let reqUrl = url.absoluteString
        // 唤起支付宝  alipays添加在info的URLtype  方便支付宝取消支付返回
        if ((reqUrl.hasPrefix("alipays://")) == true) || ((reqUrl.hasPrefix("alipay://")) == true) {
            // 解决跳转到本地支付宝App不返回的问题  解码URL 将alipays替换成功自己的URL Schemes  在taget -> info -> URL Types设置
            let aliURLString = reqUrl.urlDecoded().replacingOccurrences(of: "alipays", with: Config.jiaYiDoctorScheme)
            let openedURL = URL.init(string: aliURLString.urlEncoded())!
            // 支付宝判定是否打开很容易导致失败 所以是直接打开
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(openedURL) { (bSucc) in
                    if bSucc == false {
                        ProgressHUD.showMessage("您还未安装支付宝App,请安装后重试")
                    }
                }
                UIApplication.shared.open(openedURL,  completionHandler: nil)
            } else {
                let bSucc = UIApplication.shared.openURL(openedURL)
                if bSucc == false {
                    ProgressHUD.showMessage("您还未安装支付宝App,请安装后重试")
                }
            }
        }
        
        var redirectUrl:String?
        // 微信的必须将redirect_url也配置为支付域名 否则也会报商户错误
        if ((reqUrl.hasPrefix("https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb")) == true && (reqUrl.hasSuffix("redirect_url=\(Config.PayDomain)://")) == false) {
            decisionHandler(.cancel)
            // 添加自己的app的scheme方便回调  如果不更新返回会跳转到浏览器去
            if ((reqUrl.contains("redirect_url=")) == true) {
                let array = reqUrl.components(separatedBy: "redirect_url=")
                if array.count == 2 {
                    endPayRedirectURL = array[1]
                    redirectUrl = array[0] + "redirect_url=\(Config.PayDomain)://"
                }
            } else {
                redirectUrl = reqUrl +  "redirect_url=\(Config.PayDomain)://"
            }
            // 设置请求信息 避免会跳转浏览器说商家信息格式错误  微信也需要重新定向加载一次
            let newRequest:NSMutableURLRequest = NSMutableURLRequest.init(url: URL.init(string: redirectUrl!)!)
            newRequest.allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields
            newRequest.setValue(Config.PayDomain, forHTTPHeaderField: "Referer")
            newRequest.url = URL.init(string: redirectUrl!)
            webView.load(newRequest as URLRequest)
            return
        }
        
        let scheme:String = navigationAction.request.url?.scheme ?? ""
        if scheme != "https" && scheme != "http" {
            decisionHandler(.cancel)
            // 加载微信页面钱要先加载自己的结果页面  但是结果页面返回会有空白页面
            //如果不展示空白页面可以在页面微信支付结果回调后发送通知刷新页面 这样不会有空白页面
            if scheme == "weixin" {
                if endPayRedirectURL != nil {
                    // 必须要再次解码 否则返回为空白页面 但是在返回还是weixin标题的空白页 只有appdelega添加这个结果刷新才不会有空白页
                    webView.load(URLRequest.init(url: URL.init(string: endPayRedirectURL!.urlDecoded())!))
                }
            }
            // 打开微信支付
            if reqUrl.hasPrefix("weixin://") {
                if UIApplication.shared.canOpenURL(url) == false {
                    ProgressHUD.showMessage("您还未安装微信App,请安装后重试")
                } else {
                    if #available(iOS 10.0, *) {
                        UIApplication.shared.open(url)
                    } else {
                        UIApplication.shared.openURL(url)
                    }
                }
            }
            return
        }
        decisionHandler(.allow)
    }