· 转载请标明出处:http://www.jianshu.com/p/5ea1a0d9ee8a
1、登录:微信、QQ、Sina,分享:微信、QQ、Sina,支付:微信支付,支付宝支付;
2、集成一起的初衷是源于分享一直是原生写的,登录用友盟之类的,回调这块觉得麻烦,就统一集成了,统一回调 URL:
appDelegate.m
3、代码结构介绍:
4、开始项目
4.1、第一步导入第三方 SDK 到项目,然后配置 info.plist,和 URL Schemes;QQ 和Wechat 注册应用一定要审核通过才有登录能力,Sina 要设置 bundleID(bundleid 和项目不一样登录会报错) 和回调 URL 才能正常登录,添加私有库;
01
cpmmand + v 到自己项目中
URL Schemes
4.2、QQ、WeChat、Sina 注册应用在 APPdelegate.m中的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[YZThirdManager thirdManager] registerApp:@"微博 APPID" WithType:YZRegisterTypeSina];
[[YZThirdManager thirdManager] registerApp:@"微信 APPID" WithType:YZRegisterTypeWechat];
[[YZThirdManager thirdManager] registerApp:@"QQ APPID" WithType:YZRegisterTypeQQ];
return YES;
}
4.3、YZThirdManager �这个工具类主要做的就是分享、登录、支付集成在一起,统一回到 URL,代码如下:
/********************
第三方管理工具类:
1、微信分享、登录、支付
2、微博分享、登录
3、QQ分享、登录
4、支付宝支付。
********************/
#import
#import
#import
#import "WXApi.h"
#import "WeiboSDK.h"
#import
/** 分享后的跳转链接 */
extern NSString * const Share_URL;
/** 分享标题 */
extern NSString * const Share_title;
/** 分享内容 */
extern NSString * const Share_desc;
/** 分享显示的图片 */
extern NSString * const Share_imageData;
/**
分享类型
*/
typedef NS_ENUM(NSInteger, YZShareType) {
YZShareTypeWechatSession, // 分享到微信好友
YZShareTypeWechatTimeLine, // 分享到微信朋友圈
YZShareTypeQQ, // 分享到QQ
YZShareTypeSina // 分享到sina微博
};
/**
注册类型
*/
typedef NS_ENUM(NSInteger, YZRegisterType) {
YZRegisterTypeWechat, // 微信
YZRegisterTypeQQ, // QQ
YZRegisterTypeSina // sina微博
};
/**
登录类型
*/
typedef NS_ENUM(NSInteger, YZOAuthType) {
YZOAuthTypeWechat, // 微信
YZOAuthTypeQQ, // QQ
YZOAuthTypeSina // sina微博
};
/**
协议
*/
@protocol YZThirdManagerDelegate
@optional
#pragma mark 第三方登录回调
/**
WeChat 登录成功
@param userDict WeChat 用户信息
*/
- (void)wechat_loginSuccess:(NSDictionary *)userDict;
/**
Sina 登录成功
@param userDict Sina 用户信息
*/
- (void)sina_loginSuccess:(NSDictionary *)userDict;
/**
QQ 登录成功
@param userDict QQ 用户信息
*/
- (void)tencent_loginSuccess:(NSDictionary *)userDict;
#pragma mark 第三方分享回调
/**
微信分享后收到微信的响应
分享是否成功 需要判断 errCode 属性 WXErrCode
@param response 响应消息
*/
- (void)wechat_shareDidReceiveResponse:(BaseResp *)response;
/**
QQ分享后收到QQ的响应
分享是否成功 需要判断 result 属性, QQApiSendResultCode
@param response 响应消息
*/
- (void)tencent_shareDidReceiveResponse:(SendMessageToQQResp *)response;
/**
微博分享后收到微博的响应
分享是否成功 需要判断 statusCode 码 WeiboSDKResponseStatusCode
@param response 响应消息
*/
- (void)sina_shareDidReceiveResponse:(WBBaseResponse *)response;
#pragma mark 支付回调
/**
微信支付后收到微信的响应,支付是否成功需要判断 errCode 属性 WXErrCode
@param response 响应消息
*/
- (void)wechat_PayDidReceiveResponse:(PayResp *)response;
/**
支付宝支付收到支付宝的响应,支付是否成功需要判断 resultDic的key resultStatus = 9000 成功
@param resultDic 结果信息
*/
- (void)aliPay_DidReceiveResponse:(NSDictionary *)resultDic;
@end
@interface YZThirdManager : NSObject
@property (nonatomic, weak) id third_delegate;
+ (instancetype)thirdManager;
/**
注册应用
@param appid 微信和QQ请传入appid,微博传入appkey
@param type 分享类型
*/
- (void)registerApp:(NSString *)appid WithType:(YZRegisterType)type;
/**
分享到第三方,示例:@{Share_URL:@"http://www.baidu.com",Share_title:@"测试分享",Share_imageData:UIImageJPEGRepresentation(img,1),Share_desc:@"文本内容"};
@param dict 分享的内容
@param type 分享的类型
*/
- (void)shareWithDictionary:(NSDictionary *)dict shareType:(YZShareType)type;
/**
第三方登录
@param type 登录的类型
*/
- (void)thirdLoginWithOAuthType:(YZOAuthType)type;
/**
第三方 URL 回调
*/
- (void)handleOpenURL:(NSURL *)url;
/**
微信支付,order 的 key 是按照 PayReq 的属性设置
@param order 订单信息
*/
- (void)thirdPayWechat:(NSDictionary *)order;
/**
支付宝支付
@param sign 签名字符串
*/
- (void)thirdAliPaySign:(NSString *)sign;
@end
//
// YZThirdManager.m
// ECGoudeli
//
// Created by Leaf on 2017/8/10.
// Copyright © 2017年 Leaf. All rights reserved.
//
/** 下面信息是需要去对应第三方开发平台注册应用获得 */
#define WX_APP_ID @"微信 APP_ID"
#define WX_APP_AppSecret @"微信 AppSecret"
#define QQ_APP_ID @"QQ APP_ID"
#define QQ_APP_KEY @"QQ APP_KEY"
#define WB_APP_KEY @"微博 APP_KEY"
#define WB_APP_Secret @"微博 APP_Secret"
#define WB_REDIRECT_URL @"http://www.sina.com"
NSString * const Share_URL = @"url";
NSString * const Share_title = @"title";
NSString * const Share_desc = @"desc";
NSString * const Share_imageData = @"imageData";
#import "YZThirdManager.h"
@interface YZThirdManager ()
{
TencentOAuth *_tencentOAuth;
NSMutableArray *_permissionArray; // QQ 需要的权限
NSData *_imageData;
NSString *_title;
NSString *_description;
NSString *_urlString;
}
@property (nonatomic, strong) AFHTTPSessionManager *manager;
@end
@implementation YZThirdManager
+ (instancetype)thirdManager {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
// 注册第三方
- (void)registerApp:(NSString *)appid WithType:(YZRegisterType)type {
switch (type) {
case YZRegisterTypeWechat:
[WXApi registerApp:appid];
break;
case YZRegisterTypeQQ:
_tencentOAuth = [[TencentOAuth alloc]initWithAppId:appid andDelegate:self];
break;
case YZRegisterTypeSina:
[WeiboSDK registerApp:appid];
break;
}
}
#pragma mark - Share: Wechat & Sina & QQ
- (void)shareWithDictionary:(NSDictionary *)dict shareType:(YZShareType)type {
_title = dict[Share_title];
_urlString = dict[Share_URL];
_description = dict[Share_desc];
_imageData = dict[Share_imageData];
if (type == YZShareTypeQQ) {
[self shareToQQ];
}else if (type == YZShareTypeWechatSession || type == YZShareTypeWechatTimeLine){
[self shareToWeChatWithWechatType:type];
}else if (type == YZShareTypeSina){
[self shareToWeibo];
}
}
/**
分享到wechat
@param wechatType 分享到微信的类型
*/
- (void)shareToWeChatWithWechatType:(YZShareType)wechatType{
SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
WXMediaMessage *message = [WXMediaMessage message];
if (_urlString.length) {
WXWebpageObject *web = [WXWebpageObject object];
web.webpageUrl = _urlString;
message.mediaObject = web;
} else {
WXImageObject *image = [WXImageObject object];
image.imageData = _imageData;
message.mediaObject = image;
req.bText = NO;
}
message.title = _title ?: @"";
message.description = _description?:@"";
// 微信分享图片大小不能超过 32K
[message setThumbImage:[UIImage imageWithData:_imageData]];
// 分享给好友
if (wechatType == YZShareTypeWechatSession) {
req.scene = WXSceneSession;
}
// 分享到朋友圈
if (wechatType == YZShareTypeWechatTimeLine) {
message.title = _description;
req.scene = WXSceneTimeline;
}
req.message = message;
[WXApi sendReq:req];
}
/**
* 分享到微博
*/
-(void)shareToWeibo{
WBMessageObject *message = [WBMessageObject message];
WBImageObject *imageObject = [WBImageObject object];
imageObject.imageData = _imageData;
message.imageObject = imageObject;
message.text = [NSString stringWithFormat:@"%@%@",_title,_urlString] ;
WBSendMessageToWeiboRequest *request = [WBSendMessageToWeiboRequest requestWithMessage:message authInfo:nil access_token:nil];
request.userInfo = nil;
[WeiboSDK sendRequest:request];
}
/**
* 分享到QQ
*/
-(void)shareToQQ{
NSURL *url = [NSURL URLWithString:_urlString];
QQBaseReq *request = nil;
if (_urlString.length) {
QQApiNewsObject *news = [QQApiNewsObject objectWithURL:url title:_title description:_description previewImageData:_imageData];
request = [SendMessageToQQReq reqWithContent:news];
} else {
QQApiImageObject *image = [[QQApiImageObject alloc] initWithData:_imageData previewImageData:_imageData title:_title description:_description];
request = [SendMessageToQQReq reqWithContent:image];
}
[QQApiInterface sendReq:request];
}
#pragma mark - OAuth: wechat & QQ & sina
-(void)thirdLoginWithOAuthType:(YZOAuthType)type {
if (type == YZOAuthTypeQQ) {
_permissionArray = [NSMutableArray arrayWithArray:@[/** 移动端获取用户信息 */
kOPEN_PERMISSION_GET_SIMPLE_USER_INFO,
]];
[_tencentOAuth authorize:_permissionArray inSafari:NO];
}else if (type == YZOAuthTypeWechat) {
SendAuthReq *req = [[SendAuthReq alloc] init];
req.scope = @"snsapi_userinfo";
req.state = @"App";
[WXApi sendReq:req];
}else if (type == YZOAuthTypeSina) {
WBAuthorizeRequest *request = [WBAuthorizeRequest request];
//回调地址与 新浪微博开放平台中 我的应用 --- 应用信息 -----高级应用 -----授权设置 ---应用回调中的url保持一致就好了
request.redirectURI = WB_REDIRECT_URL;
[WeiboSDK sendRequest:request];
}
}
#pragma mark - WXApiDelegate & QQApiInterfaceDelegate & WeiboSDKDelegate
/*
1、处理来至QQ的响应
2、处理来至微信的响应
*/
- (void)onResp:(BaseResp *)resp {
if ([resp isKindOfClass:[BaseResp class]]) {
// 处理来自微信的响应
if ([resp isKindOfClass:[SendAuthResp class]]) {
if (resp.errCode == WXSuccess) {
SendAuthResp *resp2 = (SendAuthResp *)resp;
[self getWechatOpenidAndToken:resp2.code];
}else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"登录失败" message:[NSString stringWithFormat:@"%@",resp.errStr] delegate:self cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
[alert show];
}
}else if ([resp isKindOfClass:[SendMessageToWXResp class]]) {
DebugLog(@"微信分享了");
if ([self.third_delegate respondsToSelector:@selector(wechat_shareDidReceiveResponse:)]) {
[self.third_delegate wechat_shareDidReceiveResponse:resp];
}
}else if ([resp isKindOfClass:[PayResp class]]) {
DebugLog(@"微信支付");
if ([self.third_delegate respondsToSelector:@selector(wechat_PayDidReceiveResponse:)]) {
[self.third_delegate wechat_PayDidReceiveResponse:(PayResp *)resp];
}
}
}else if ([resp isKindOfClass:[QQBaseResp class]]) {
// 处理来自QQ的响应
if ([resp isKindOfClass:[SendMessageToQQResp class]]) {
DebugLog(@"share:QQ分享");
if ([self.third_delegate respondsToSelector:@selector(tencent_shareDidReceiveResponse:)]) {
[self.third_delegate tencent_shareDidReceiveResponse:(SendMessageToQQResp *)resp];
}
}
}
}
/*
1、处理来至QQ的请求
2、处理来至微信的请求
*/
- (void)onReq:(BaseReq *)req {
if ([req isKindOfClass:[BaseReq class]]) {
// 处理来自微信的请求
}else if ([req isKindOfClass:[QQBaseReq class]]) {
// 处理来自QQ的请求
}
}
/**
处理QQ在线状态的回调
*/
- (void)isOnlineResponse:(NSDictionary *)response{
}
/**
收到一个来自微博客户端程序的请求
*/
- (void)didReceiveWeiboRequest:(WBBaseRequest *)request{
DebugLog(@"request %@",request);
}
/**
收到一个来自微博客户端程序的响应
*/
- (void)didReceiveWeiboResponse:(WBBaseResponse *)response{
if ([response isKindOfClass:[WBSendMessageToWeiboResponse class]]) {
DebugLog(@"share:微博分享");
if ([self.third_delegate respondsToSelector:@selector(sina_shareDidReceiveResponse:)]) {
[self.third_delegate sina_shareDidReceiveResponse:response];
}
}else if ([response isKindOfClass:[WBAuthorizeResponse class]]) {
DebugLog(@"微博登录");
if (response.statusCode == WeiboSDKResponseStatusCodeSuccess) {
NSString *uid = [(WBAuthorizeResponse *)response userID];
NSString *token = [(WBAuthorizeResponse *)response accessToken];
[self getSinaUserInfo:uid token:token];
}else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"登录失败" message:[NSString stringWithFormat:@"statusCode: %zd",response.statusCode] delegate:self cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
[alert show];
}
}
}
#pragma mark - TencentLoginDelegate
//委托
- (void)tencentDidLogin
{
[_tencentOAuth getUserInfo];
}
- (void)getUserInfoResponse:(APIResponse *)response
{
if (response.retCode == URLREQUEST_SUCCEED) {
NSDictionary *userDict = @{@"third_id" : [_tencentOAuth openId],
@"third_name" : [response.jsonResponse valueForKeyPath:@"nickname"],
@"third_image" : [response.jsonResponse valueForKeyPath:@"figureurl_qq_2"],
@"access_token": [_tencentOAuth accessToken]};
if ([self.third_delegate respondsToSelector:@selector(tencent_loginSuccess:)]) {
[self.third_delegate tencent_loginSuccess:userDict];
}
DebugLog(@"userDict %@",userDict);
} else {
DebugLog(@"登录失败!");
}
}
- (void)tencentDidLogout {
}
/**
* 登录失败后的回调
* param cancelled 代表用户是否主动退出登录
*/
- (void)tencentDidNotLogin:(BOOL)cancelled {
}
- (void)tencentDidNotNetWork {
}
#pragma mark - 获取第三方用户信息
// 获取 Sina 的用户信息
- (void)getSinaUserInfo:(NSString *)uid token:(NSString *)token {
NSString *urlStr =[NSString stringWithFormat:@"https://api.weibo.com/2/users/show.json?uid=%@&access_token=%@&source=%@",uid,token,WB_APP_KEY];
__weak typeof(self) weakself = self;
[self.manager GET:urlStr parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
NSDictionary *userDict = @{
@"third_id" : [dic valueForKeyPath:@"idstr"],
@"third_name" : [dic valueForKeyPath:@"screen_name"],
@"third_image": [dic valueForKeyPath:@"avatar_hd"],
@"access_token":token
};
if ([weakself.third_delegate respondsToSelector:@selector(sina_loginSuccess:)]) {
[weakself.third_delegate sina_loginSuccess:userDict];
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
DebugLog(@"error %@",error);
}];
}
//通过 appid secret 认证code . 来发送获取 access_token的请求
- (void)getWechatOpenidAndToken:(NSString *)code {
NSString *urlStr = [NSString stringWithFormat:@"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code",WX_APP_ID,WX_APP_AppSecret,code];
__weak typeof(self) weakself = self;
[self.manager GET:urlStr parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
/*
access_token 接口调用凭证
expires_in access_token接口调用凭证超时时间,单位(秒)
refresh_token 用户刷新access_token
openid 授权用户唯一标识
scope 用户授权的作用域,使用逗号(,)分隔
unionid 当且仅当该移动应用已获得该用户的userinfo授权时,才会出现该字段
*/
NSString* accessToken=[dic valueForKey:@"access_token"];
NSString* openID=[dic valueForKey:@"openid"];
[weakself getWechatUserInfo:accessToken openid:openID];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
DebugLog(@"error %@",error.localizedFailureReason);
}];
}
// 通过 access_token 获取微信用户信息
- (void)getWechatUserInfo:(NSString *)token openid:(NSString *)openid {
NSString *urlStr = [NSString stringWithFormat:@"https://api.weixin.qq.com/sns/userinfo?access_token=%@&openid=%@",token,openid];
__weak typeof(self) weakself = self;
[self.manager GET:urlStr parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSDictionary *dic = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
NSDictionary *userDict = @{
@"way" : @"1", // 登录方式:微信
@"openid" : openid, // 用户唯一标识
@"headimgurl": dic[@"headimgurl"], // 头像URL
@"nickname" : dic[@"nickname"], // 昵称
@"sex" : [dic[@"sex"] stringValue], // 性别
@"country" : dic[@"country"], // 国家
@"province" : dic[@"province"], // 省份
@"city" : dic[@"city"], // 城市
@"language" : dic[@"language"] // 语言
};
if ([weakself.third_delegate respondsToSelector:@selector(wechat_loginSuccess:)]) {
[weakself.third_delegate wechat_loginSuccess:userDict];
}
DebugLog(@"userDict = %@",userDict);
}failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
DebugLog(@"error %ld",(long)error.code);
}];
}
#pragma mark - Wechat Pay & AliPay
- (void)thirdPayWechat:(NSDictionary *)order {
PayReq *payOrder = [[PayReq alloc] init];
payOrder.openID = [order objectForKey:@"openID"];
payOrder.partnerId = [order objectForKey:@"partnerId"];
payOrder.prepayId = [order objectForKey:@"prepayId"];
payOrder.package = [order objectForKey:@"package"];
payOrder.nonceStr = [order objectForKey:@"nonceStr"];
payOrder.timeStamp = (UInt32)[[order objectForKey:@"timeStamp"] integerValue];
payOrder.sign = [order objectForKey:@"sign"];
if (![WXApi sendReq:payOrder]) {
DebugLog(@"支付出错,请稍后再试");
}
}
- (void)thirdAliPaySign:(NSString *)sign {
__weak typeof(self) weakself = self;
[[AlipaySDK defaultService] payOrder:sign fromScheme:@"com.kyx.eduParents" callback:^(NSDictionary *resultDic) {
[weakself didReceiveAliPayRequest:resultDic];
}];
}
#pragma mark - Private
- (void)didReceiveAliPayRequest:(NSDictionary *)resultDic {
if ([self.third_delegate respondsToSelector:@selector(aliPay_DidReceiveResponse:)]) {
[self.third_delegate aliPay_DidReceiveResponse:resultDic];
}
}
#pragma mark - 第三方请求结果URL回调
-(void)handleOpenURL:(NSURL *)url {
__weak typeof(self) weakself = self;
if ([url.scheme isEqualToString:WX_APP_ID]) {
[WXApi handleOpenURL:url delegate:self];
} else if ([url.scheme isEqualToString:[NSString stringWithFormat:@"tencent%@",QQ_APP_ID]]) {
[QQApiInterface handleOpenURL:url delegate:self];
[TencentOAuth HandleOpenURL:url];
}else if ([url.scheme isEqualToString:[NSString stringWithFormat:@"wb%@",WB_APP_KEY]]) {
[WeiboSDK handleOpenURL:url delegate:self];
} else if ([url.host isEqualToString:@"safepay"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
// 处理支付宝支付结果
[weakself didReceiveAliPayRequest:resultDic];
}];
} else if ([url.host isEqualToString:@"alipayclient"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
// 处理支付宝支付结果
[weakself didReceiveAliPayRequest:resultDic];
}];
}
}
#pragma mark - 懒加载
-(AFHTTPSessionManager *)manager {
if (_manager == nil) {
_manager = [AFHTTPSessionManager manager];
_manager.requestSerializer = [AFJSONRequestSerializer serializer];//请求
_manager.responseSerializer = [AFHTTPResponseSerializer serializer];//响应
_manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/json",@"text/plain", nil];
}
return _manager;
}
@end
5、登录成功通过代理回调返回用户信息出来:
6、上面的 YZThirdManager 类的代码已经贴出来了,如果要demo,下面回复邮箱地址