[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)")
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容