[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"
    /// 用于保持请求和回调的状态,授权请求后原样带回给第三方
    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)")
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容