iOS开发中, 如果不想接入支付的SDK 或者 商品只能走内购, 但是又不想要走内购(不想给苹果手续费...), 那么就可以考虑使用支付宝和微信的H5支付.
这里的支付宝支付和微信支付 指的是通过web页, 传给客户端包括订单和其他信息的字符串, 客户端拿到这个特殊的字符串, 进行处理, 然后通过scheme打开支付宝或者微信客户端进行支付.
写在前面:
一般通过WKWebView的
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)
方法来拦截要加载的url, 支付宝支付带有订单信息的URL 前缀为 alipay
或者 alipays
. 微信的是 https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb
.
我们这样子写是有问题的. 这样子写递交审核, 机审的时候, 会扫描出来alipay
tenpay
的字眼, 可以判定你没有走内购而拒绝你的递交, 甚至会延迟审核. 所以我们要做的是, 把有关alipay 和 tenpay 这些字符串拆成一个个的字符, 然后用到的时候再进行组装比较, 这样子可以绕过苹果的机审.
进入正题
适配支付宝客户端支付:
支付宝支付比较简单一些, 因为 如果安装了支付宝 可以在支付宝客户端上面进行支付, 如果没有安装, 也可以在网页上面支付.
首先截取到支付宝支付加载的URL
// 某宝相关字符串
let compareArrayCollect1: [Character] = ["a", "l", "i", "p", "a", "y"]
let compareArrayCollect2: [Character] = ["a", "l", "i", "p", "a", "y", "s"]
if loadURLString.hasPrefix(charArray: compareArrayCollect1) || loadURLString.hasPrefix(charArray: compareArrayCollect2) {
//某宝 stringUrl 为webView所加载的url: navigationAction.request.url?.absoluteString.removingPercentEncoding
if let url = stringUrl, let changeURL = self.changeURLSchemeStr(urlStr: url) {
// 跳转
UIApplication.shared.openURL(changeURL)
}
}
changeURLSchemeStr
方法作用是:
- 对url进行解码.
- 转换成字典.
- 替换
fromAppUrlScheme
的值为当前app对应的scheme. - 再进行编码 并返回新的URL.
其中替换fromAppUrlScheme
的作用是 跳转支付宝 交易结束之后, 支付宝要通过你app的scheme 跳回到你的app
代码如下:
private func changeURLSchemeStr(urlStr: String) -> URL? {
if urlStr.contains("fromAppUrlScheme") {
guard var rmPerEncStr = urlStr.removingPercentEncoding else { return nil }
guard let temDic = self.dictionaryWithUrlString(urlString: rmPerEncStr) else { return nil }
guard let temValue = temDic["fromAppUrlScheme"] as? String else { return nil }
rmPerEncStr = rmPerEncStr.replacingOccurrences(of: temValue, with: "yourAppScheme")
guard let perEncodingStr = rmPerEncStr.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else { return nil }
return URL(string: perEncodingStr)
}
return nil
}
private func dictionaryWithUrlString(urlString: String) -> [String: Any]? {
guard urlString.count != 0 else { return nil }
let array = urlString.components(separatedBy: "?")
guard array.count == 2 else { return nil }
guard let paramStr = array.last else { return nil }
let rmPerEnStr = paramStr.removingPercentEncoding
return rmPerEnStr?.convertToDictionary()
}
微信支付
微信的H5支付稍微要麻烦一些. 主要麻烦的点在于设置 微信的回跳地址 redirect_url 和授权域名 Referer.
- redirect_url 的设置最好设置为本app的urlScheme, Referer为后端或者产品在注册微信支付时所填写的授权域名.
- redirect_url: 作用为 当app完成支付(不论是否支付成功), 所跳转的页面, 如果不设置, 那么支付完成之后就会直接跳转到Safari, 打开的是一个支付是否成功的验证页面, 设置redirect_url的目的为了支付完成之后 正常跳转到app 并在app中的web页面中打开支付是否成功的验证页面. 所以在这里也要记录一下原始的redirect_url. 用户回跳之后, 要加载显示的验证页面.
- Referer: 官方文档上面有说明, Referer如果不设置, 跳转到微信, 打不开支付页面, 验证不通过.
if loadURLString.hasPrefix("https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb") && !loadURLString.contains("redirect_url=your_referer_name//paycallback/") {
// 某信
// 先判断有没有安装微信
let someXinCollect: [Character] = ["w", "e", "i", "x", "i", "n", ":", "/", "/"]
let someXinStr = String.charCollectTransToString(charArray: someXinCollect)
guard let sonXinURL = URL(string: someXinStr) else { return false }
if !UIApplication.shared.canOpenURL(sonXinURL) {
showToast(text: "请安装微信客户端")
return false
}
如果当前加载的url的前缀有某信支付的连接并且不包含 “redirect_url=your_referer_name//paycallback/”的字符串, 那么先判断是否安装微信, 如果没有给出toast提示 需要安装微信客户端.
// 拼接rturl
var rtUrl: String = ""
let authHost = "your_refere_name" //你的微信授权域名
if loadURLString.contains(rtURLStr) {
guard let redirectRange = loadURLString.range(of: rtURLStr) else { return true }
let location: Int = loadURLString.distance(from: loadURLString.startIndex, to: redirectRange.lowerBound)
rtUrl = loadURLString.substring(to: location - 1) + "\(rtURLStr)\(authHost):\(charCallbackCollectStr)"
originalRtUrlStr = loadURLString.substring(from: location +
rtURLStr.count)
} else {
rtUrl = loadURLString + "\(rtURLStr)\(authHost):\(charCallbackCollectStr))" // 替换为你自己的授权域名(也就是你的scheme, 微信完成支付时 会按照这个scheme跳转回你的app)
}
guard let requestURL = URL(string: rtUrl) else { return true }
var request = navigationAction.request
request.url = requestURL
// 在这一步 设置request的header信息. 加入Referer 参数
if var headerField = request.allHTTPHeaderFields {
headerField["Referer"] = authHost
}
webView.load(request)
return false
这一步主要做的工作是: 替换掉前端原本传过来redirect_url, 并且记录原始的redirect_url: originalRtUrlStr. 然后拼接好已经替换的loadURL, 设置request, 并且在请求头中加入Referer参数 进行重新加载.
下一步:
if loadURLString.hasPrefix(charArray: "your_referer_name") {
guard let requestURL = URL(string: originalRtUrlStr) else { return false }
var request = navigationAction.request
request.url = requestURL
webView.load(request)
return false
}
if loadURLString.contains("weixin://") {
UIApplication.shared.openURL(loadURL)
}
return true
这一步是如果loadURL, 是以你的授权域名为开头, 那么就加载这个页面. (实际上是微信支付 验证是否成功的页面.), 最后如果加载的url是以weixin://开头, 这个链接就是带有微信支付信息的连接, 就打开微信进行支付操作.