使用
wechatLogin.rx.tap //微信登录
.throttle(RxTimeInterval.milliseconds(300), scheduler: MainScheduler.instance)
.subscribe(onNext: { [weak self] _ in
guard let self = self else { return }
WechatUtil.shared.login(viewController: self)
}).disposed(by: disposeBag)
WechatUtil.shared.respSubject
.map(Reactor.Action.wechatLogin)
.bind(to: reactor.action)
.disposed(by: disposeBag)
WechatUtil.shared.respSubject.observe(on: MainScheduler.asyncInstance)
.subscribe(onNext: { [weak self] baseResp in
guard let payResp = baseResp as? PayResp else { return }
switch payResp.errCode {
case 0: // 0 成功 展示页面成功
HUD.success()
default:
HUD.error(title: payResp.errStr, message: nil)
}
}).disposed(by: disposeBag)
配置
SDK
pod 'WechatOpenSDK'
Xcode
-
配置 URL type (Target - Info - URL Types)
-
打开Associated Domains开关,将Universal Links域名加到配置上
applinks:abc.com
apple-app-site-association
{
"applinks": {
"apps": [],
"details": [
{
"appID": "ABCDEFG.com.xx.xx",
"paths": [
"/app/*"
]
}
]
}
}
Nginx
location ~ /apple-app-site-association {
default_type application/json;
alias /root/ios/apple-app-site-association;
}
SceneDelegate
// MARK: SceneDelegate + open URL
extension SceneDelegate {
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
log.info(userActivity)
WechatUtil.shared.openUniversalLink(userActivity: userActivity)
}
func scene(_ scene: UIScene, willContinueUserActivityWithType userActivityType: String) {
log.info(userActivityType)
WechatUtil.shared.openUniversalLink(userActivity: NSUserActivity(activityType: userActivityType))
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
URLContexts.forEach { (context: UIOpenURLContext) in
log.info(context)
let url = context.url
if url.host == "safepay" {
// 支付宝支付
ZhifubaoUtil.openSafepayURL(url: url)
} else if url.host == "pay" {
// 微信支付
WechatUtil.shared.openURL(url: url)
}
}
}
}
工具类
//
// WechatUtil.swift
//
import Foundation
import RxSwift
protocol WechatDelegate: NSObjectProtocol {
func onReq(req: BaseReq)
func onResp(resp: BaseResp)
}
/*
iOS接入指南
- https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html
- pod 'WechatOpenSDK'
- 配置应用的 Universal Links (微信要求 App 配置的paths必须加上通配符) https://xx.xxx.com/app/
- 打开Associated Domains开关,将Universal Links域名加到配置上
- 配置 URL type (Target - Info - URL Types) (URL scheme)
- Info.plist 配置 LSApplicationQueriesSchemes 添加 weixin weixinULAPI weixinURLParamsAPI
*/
class WechatUtil: NSObject {
/// 应用唯一标识,在微信开放平台提交应用审核通过后获得
static let appid = "123"
static let openID = "456"
static let secret = "789"
static let universalLink = "https://abc.def.com/app/"
/// 应用授权作用域,如获取用户个人信息则填写 snsapi_userinfo
static let scope = "snsapi_userinfo"
/// 用于保持请求和回调的状态,授权请求后原样带回给第三方
static let state = "xxx"
public static let shared = WechatUtil()
weak var delegate: WechatDelegate?
/**
- AsyncSubject<String>.init() 将在源 Observable 产生完成事件后,发出最后一个元素
- PublishSubject<String>.init() 将对观察者发送订阅后产生的元素,而在订阅前发出的元素将不会发送给观察者。
- ReplaySubject<String>.create(bufferSize: 8) 将对观察者发送缓存的8个元素 (或者全部的元素),无论观察者是何时进行订阅的。
- BehaviorSubject<String>.init(value: "") 对观察者进行订阅时,它会将源 Observable 中最新的元素发送出来(或发送默认元素)
*/
let respSubject = PublishSubject<BaseResp>.init()
private override init() {
super.init()
}
}
extension WechatUtil {
/// 是否安装微信
var isInstalled: Bool {
return WXApi.isWXAppInstalled()
}
/// 微信注册
func register(){
#if DEBUG
WXApi.startLog(by: WXLogLevel.detail) { msg in
log.debug("WechatUtil.WXLog === \(msg)")
}
#endif
WXApi.registerApp(WechatUtil.appid, universalLink: WechatUtil.universalLink)
// WXApi.checkUniversalLinkReady { step, result in
// log.console("WechatUtil.step === \(step); result: \(result)")
// }
}
@discardableResult
func openURL(url: URL) -> Bool {
return WXApi.handleOpen(url, delegate: WechatUtil.shared)
}
@discardableResult
func openUniversalLink(userActivity: NSUserActivity) -> Bool {
return WXApi.handleOpenUniversalLink(userActivity, delegate: WechatUtil.shared)
}
/// 微信登录
/// https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html
func login(viewController: UIViewController){
let req = SendAuthReq()
req.scope = WechatUtil.scope
req.state = WechatUtil.state
//req.openID = openID
WXApi.sendAuthReq(req, viewController: viewController, delegate: WechatUtil.shared, completion: nil)
}
/// 微信支付
/// https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml
func pay(partnerId: String, prepayId: String, nonceStr: String, timeStamp: UInt32, package: String, sign: String) {
let req = PayReq()
//由用户微信号和AppID组成的唯一标识,用于校验微信用户
req.openID = WechatUtil.appid
/* 商家向财付通申请的商家id */
req.partnerId = partnerId
// 预支付订单这个是后台跟微信服务器交互后,微信服务器传给你们服务器的,你们服务器再传给你
req.prepayId = prepayId
/* 随机串,防重发 */
req.nonceStr = nonceStr // UUID().uuidString
/* 时间戳,防重发 */
req.timeStamp = timeStamp // UInt32(Date.current.timeIntervalSince1970)
/* 商家根据财付通文档填写的数据和签名 */
req.package = package
/* 商家根据微信开放平台文档对数据做的签名 */
req.sign = sign
//发送请求到微信,等待微信返回onResp
WXApi.send(req)
}
}
// MARK: WXApiDelegate
extension WechatUtil: WXApiDelegate {
func onReq(_ req: BaseReq) {
delegate?.onReq(req: req)
if let wxReq = req as? PayReq {
log.info("WechatUtil.onReq ==== PayReq 支付 \(wxReq)")
} else if let wxReq = req as? WXOfflinePayReq {
log.info("WechatUtil.onReq ==== WXOfflinePayReq 离线支付 \(wxReq)")
} else if let wxReq = req as? WXNontaxPayReq {
log.info("WechatUtil.onReq ==== WXNontaxPayReq 调用非税支付 \(wxReq)")
} else if let wxReq = req as? WXPayInsuranceReq {
log.info("WechatUtil.onReq ==== WXPayInsuranceReq 医保支付 \(wxReq)")
} else if let wxReq = req as? SendAuthReq {
log.info("WechatUtil.onReq ==== SendAuthReq \(wxReq)")
} else if let wxReq = req as? SendMessageToWXReq {
log.info("WechatUtil.onReq ==== SendMessageToWXReq \(wxReq)")
} else if let wxReq = req as? GetMessageFromWXReq {
log.info("WechatUtil.onReq ==== GetMessageFromWXReq \(wxReq)")
} else if let wxReq = req as? ShowMessageFromWXReq {
log.info("WechatUtil.onReq ==== ShowMessageFromWXReq \(wxReq)")
} else if let wxReq = req as? LaunchFromWXReq {
log.info("WechatUtil.onReq ==== LaunchFromWXReq \(wxReq)")
} else {
log.info("WechatUtil.onReq ==== \(req)")
}
}
func onResp(_ resp: BaseResp) {
delegate?.onResp(resp: resp)
respSubject.onNext(resp)
if let wxResp = resp as? PayResp {
log.info("WechatUtil.onResp ==== PayResp \(wxResp)")
//0 成功 展示页面成功
//-1 错误 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常原因等
//-2 用户取消 无需处理。发生场景:用户不支付了,点击取消,返回APP
log.info("WechatUtil.onResp ==== SendAuthResp \(wxResp.errCode)")
} else if let wxResp = resp as? WXOfflinePayResp {
log.info("WechatUtil.onResp ==== WXOfflinePayResp \(wxResp)")
} else if let wxResp = resp as? WXNontaxPayResp {
log.info("WechatUtil.onResp ==== WXNontaxPayResp \(wxResp)")
} else if let wxResp = resp as? WXPayInsuranceResp {
log.info("WechatUtil.onResp ==== WXPayInsuranceResp \(wxResp)")
} else if let wxResp = resp as? SendAuthResp {
log.info("WechatUtil.onResp ==== SendAuthResp \(wxResp)")
// ERR_OK = 0(用户同意) ERR_AUTH_DENIED = -4(用户拒绝授权) ERR_USER_CANCEL = -2(用户取消)
log.info("WechatUtil.onResp ==== SendAuthResp \(wxResp.errCode)")
// 用户换取 access_token 的 code,仅在 ErrCode 为 0 时有效
log.info("WechatUtil.onResp ==== SendAuthResp \(wxResp.code)")
} else if let wxResp = resp as? SendMessageToWXResp {
log.info("WechatUtil.onResp ==== SendMessageToWXResp \(wxResp)")
} else if let wxResp = resp as? GetMessageFromWXResp {
log.info("WechatUtil.onResp ==== GetMessageFromWXResp \(wxResp)")
} else if let wxResp = resp as? ShowMessageFromWXResp {
log.info("WechatUtil.onResp ==== ShowMessageFromWXResp \(wxResp)")
} else {
log.info("WechatUtil.onResp ==== \(resp)")
}
}
}