使用
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)
image.png -
打開Associated Domains開關讨衣,將Universal Links域名加到配置上
applinks:abc.com
image.png
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"
/// 用于保持請求和回調的狀態(tài)壁酬,授權請求后原樣帶回給第三方
static let state = "xxx"
public static let shared = WechatUtil()
weak var delegate: WechatDelegate?
/**
- AsyncSubject<String>.init() 將在源 Observable 產(chǎn)生完成事件后厅篓,發(fā)出最后一個元素
- PublishSubject<String>.init() 將對觀察者發(fā)送訂閱后產(chǎn)生的元素该贾,而在訂閱前發(fā)出的元素將不會發(fā)送給觀察者。
- ReplaySubject<String>.create(bufferSize: 8) 將對觀察者發(fā)送緩存的8個元素 (或者全部的元素)性誉,無論觀察者是何時進行訂閱的窿吩。
- BehaviorSubject<String>.init(value: "") 對觀察者進行訂閱時,它會將源 Observable 中最新的元素發(fā)送出來(或發(fā)送默認元素)
*/
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
/* 隨機串倾哺,防重發(fā) */
req.nonceStr = nonceStr // UUID().uuidString
/* 時間戳轧邪,防重發(fā) */
req.timeStamp = timeStamp // UInt32(Date.current.timeIntervalSince1970)
/* 商家根據(jù)財付通文檔填寫的數(shù)據(jù)和簽名 */
req.package = package
/* 商家根據(jù)微信開放平臺文檔對數(shù)據(jù)做的簽名 */
req.sign = sign
//發(fā)送請求到微信,等待微信返回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 醫(yī)保支付 \(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 用戶取消 無需處理硕糊。發(fā)生場景:用戶不支付了,點擊取消申尤,返回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)")
}
}
}