[Swift] iOS 集成微信 SDK (登錄 / 支付)

使用

        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)")
        }
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末衙耕,一起剝皮案震驚了整個濱河市昧穿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌橙喘,老刑警劉巖时鸵,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡饰潜,警方通過查閱死者的電腦和手機初坠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彭雾,“玉大人碟刺,你說我怎么就攤上這事∈碓停” “怎么了半沽?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吴菠。 經(jīng)常有香客問我者填,道長,這世上最難降的妖魔是什么做葵? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任占哟,我火速辦了婚禮,結果婚禮上酿矢,老公的妹妹穿的比我還像新娘榨乎。我一直安慰自己,他們只是感情好瘫筐,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布谬哀。 她就那樣靜靜地躺著,像睡著了一般严肪。 火紅的嫁衣襯著肌膚如雪史煎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天驳糯,我揣著相機與錄音篇梭,去河邊找鬼。 笑死酝枢,一個胖子當著我的面吹牛恬偷,可吹牛的內容都是我干的。 我是一名探鬼主播帘睦,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼袍患,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了竣付?” 一聲冷哼從身側響起诡延,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎古胆,沒想到半個月后肆良,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筛璧,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年惹恃,在試婚紗的時候發(fā)現(xiàn)自己被綠了夭谤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡巫糙,死狀恐怖朗儒,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情参淹,我是刑警寧澤采蚀,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站承二,受9級特大地震影響榆鼠,放射性物質發(fā)生泄漏。R本人自食惡果不足惜亥鸠,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一妆够、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧负蚊,春花似錦神妹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至伤极,卻和暖如春蛹找,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哨坪。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工庸疾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人当编。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓届慈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忿偷。 傳聞我的和親對象是個殘疾皇子金顿,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內容