iOS 接入微信SDK封装的工具类HHWeixinSDK(微信登录,微信好友和朋友圈分享,微信支付(客户端签名))

SDK下载

这里是微信官方的SDK下载接入流程文档

手动集成的话需要添加依赖库

将SDK文件中包含的 libWeChatSDK.a,WXApi.h,WXApiObject.h 三个文件添加到你所建的工程中,在Build Phases下面的Link Binary With Libraries项中添加下面所需的依赖库:

661823-7bc9bc5416528645.png

工程配置属性

在你的工程文件中选择Build Setting,在"Other Linker Flags"中加入"-Objc -all_load"

设置URL Scheme

修改 info.plist 文件 URL types 项中后面的URL Schemes内容为"wx+ appid",例如:wx1234567890
(我这里设置了支付宝,qq,微信,微博,只用到微信的话,就添加微信的就ok)


2EA80578-7343-433E-8803-73E4E826BE11.png

添加URL Schemes白名单

在“Info.plist”中增加一个LSApplicationQueriesSchemes值,设置为array, 添加微信需要的几个item:
wechat
weixin

签名安全问题(这里是客户端来签名的)

这里后台只生成了prepayId提供给我们,签名是在我们iOS客户端来签名的,(为什么呢?因为我们后台不会生成签名╮(╯﹏╰)╭),需要微信给我们的AppID,密钥和商户号,跟支付宝接入一样,签名在客户端来做是有点安全问题的,具体请参考我封装的HHAliPaySDK,签名放在服务端来做,提供给我们,既方便又安全,但是没办法,他做不来,只能我们来做,希望你们后台人员比较给力.

然后就就可以愉快的写代码了

创建了一个继承于NSObject的 HHWeixinSDK工具类(涉及微信登录,微信好友和朋友圈分享,微信支付),而且我在其中封装了一下微信支付时向后台请求prepayId的方法和处理回调结果用到的方法.
ps:这个类中有网络请求,HHttpManager是我自己对AFNetworking3.0的二次封装,感兴趣的可以看一看,不感兴趣的同学可以用AFN或者自己的网络框架.

.h文件

#import <Foundation/Foundation.h>

@interface HHWeixinSDK : NSObject
/**
 实例化
 */
+(instancetype) shareInstance;

/**
 初始化微信SDK
 */
+(void)initSDK;

/**
 打开其他app的回调
 */
+(BOOL)handleOpenURL:(NSURL *)url;

/** 
 调用微信登录接口 
 */
+(void)sendWeixinLoginRequest;


/** 
 查看微信是否安装 
 */
+ (BOOL)isWeiXinInstall;

/**
 根据订单信息向后台申请prepayId以调用微信支付接口
 
 @param userId   用户登录app的标识id
 @param orderId  订单号,由后台提供
 @param account  金额
 */
+ (void)sendWeixinPayRequestWithUserId:(NSString *)userId
                               orderId:(NSString *)orderId
                               account:(NSString *)account;

/**
 分享到微信好友
 
 @param title    分享的文字标题
 @param content  分享的文字内容
 @param urlStr   分享的图片URL字符串
 */
+ (void)shareToWeixinFriendWithTitle:(NSString *)title
                             Content:(NSString *)content
                         imageURLStr:(NSString *)urlStr;

/**
 分享到微信朋友圈
 
 @param title    分享的文字标题
 @param content  分享的文字内容
 @param urlStr   分享的图片URL字符串
 */
+ (void)shareToWeixinFriendsCircleWithTitle:(NSString *)title
                                    Content:(NSString *)content
                                imageURLStr:(NSString *)urlStr;

@end

.m文件

#import "HHWeixinSDK.h"
#import "WXApi.h"
//MD5加密需要导入的头文件
#import <CommonCrypto/CommonCrypto.h>

//微信接口
#define Weixin_GetAccessTokenURL    @"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code"
#define Weixin_isAccessTokenCanUse     @"https://api.weixin.qq.com/sns/auth?access_token=%@&openid=%@"
#define Weixin_UseRefreshToken      @"https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=wx8daa93036f9c7721&grant_type=refresh_token&refresh_token=%@"
#define Weixin_GetUserInformation  @"https://api.weixin.qq.com/sns/userinfo?access_token=%@&openid=%@"


@interface HHWeixinSDK ()<WXApiDelegate>

@end

@implementation HHWeixinSDK

static NSString * safeURL(NSString * origin) {
    return [origin stringByAddingPercentEscapesUsingEncoding:
             NSUTF8StringEncoding];
}

static HHWeixinSDK* _instance = nil;

//单例
+(instancetype) shareInstance{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init] ;
    }) ;
    
    return _instance ;
}

+(id) allocWithZone:(struct _NSZone *)zone{
    return [HHWeixinSDK shareInstance] ;
}

-(id) copyWithZone:(NSZone *)zone{
    return [HHWeixinSDK shareInstance] ;//return _instance;
}

-(id) mutablecopyWithZone:(NSZone *)zone{
    return [HHWeixinSDK shareInstance] ;
}

#pragma mark - 回调
-(void)onReq:(BaseReq *)req{
    
}

-(void)onResp:(BaseResp *)resp{
    /** 微信登录 */
    if([resp isKindOfClass:[SendAuthResp class]]){
        if (resp.errCode == 0) {
            [self loginWeixinSuccessWithBaseResp:resp];
        }else{
            //登录失败
        }
    }
    
    /** 微信支付 */
    if ([resp isKindOfClass:[PayResp class]]){
        [self handleWeixinPayCallBackResultWithPayResp:resp];
    }
}

+(void)initSDK {
    // 打开微信的登录
     [WXApi registerApp:Weixin_AppID];
}

+(BOOL)handleOpenURL:(NSURL *)url {
    return [WXApi handleOpenURL:url delegate:[self shareInstance]];
}

+(void)sendWeixinLoginRequest{
    //构造SendAuthReq结构体
    SendAuthReq* req =[[SendAuthReq alloc ] init ];
    req.scope = @"snsapi_userinfo" ;
    req.state = @"123" ;
    //第三方向微信终端发送一个SendAuthReq消息结构
    [WXApi sendReq:req];
}

+ (BOOL)isWeiXinInstall{
    return  [WXApi isWXAppInstalled];
}

#pragma mark - 微信登录成功获取token
-(void)loginWeixinSuccessWithBaseResp:(BaseResp *)resp{
    SendAuthResp  *auth = (SendAuthResp*)resp;
    NSString *code = auth.code;
    NSLog(@"code:%@", code);
    //Weixin_AppID和Weixin_AppSecret是微信申请下发的.
    NSString *str = [NSString stringWithFormat:Weixin_GetAccessTokenURL,Weixin_AppID,Weixin_AppSecret,code];
    [HHttpManager GET:str success:^(id responseObject) {
        NSString *access_token = responseObject[@"access_token"];
        NSString *refresh_token = responseObject[@"refresh_token"];
        NSString *openid = responseObject[@"openid"];
        [self isAccess_tokenCanUseWithAccess_token:access_token openID:openid completionHandler:^(BOOL isCanUse) {
            if (isCanUse) {
                [self getUserInformationWithAccess_token:access_token openID:openid];
            }else{
                [self useRefresh_token:refresh_token];
            }
        }];
        
    } failure:^(NSError * error) {
        NSLog(@"请求失败--%@",error);
    }];
    
}

#pragma mark - 判断access_token是否过期
- (void)isAccess_tokenCanUseWithAccess_token:(NSString *)access_token openID:(NSString *)openID completionHandler:(void(^)(BOOL isCanUse))completeHandler{
    NSString *strOfSeeAccess_tokenCanUse = [NSString stringWithFormat:Weixin_isAccessTokenCanUse, access_token,openID];
    
    [HHttpManager GET:strOfSeeAccess_tokenCanUse success:^(id responseObject) {
        if ([responseObject[@"errmsg"] isEqualToString:@"ok"]) {
            completeHandler(YES);
        }else{
            completeHandler(NO);
        }
    } failure:^(NSError * error) {
        NSLog(@"请求失败--%@",error);
        completeHandler(NO);
    }];
}

#pragma mark - 若过期,使用refresh_token获取新的access_token
- (void)useRefresh_token:(NSString *)refresh_token{
    NSString *strOfUseRefresh_token = [NSString stringWithFormat:Weixin_UseRefreshToken, refresh_token];
    NSLog(@"%@", strOfUseRefresh_token);

    [HHttpManager GET:strOfUseRefresh_token success:^(id responseObject) {
        NSString *openid = responseObject[@"openid"];
        NSString *access_token = responseObject[@"access_token"];
        NSString *refresh_tokenNew = responseObject[@"refresh_token"];
        [self isAccess_tokenCanUseWithAccess_token:access_token openID:openid completionHandler:^(BOOL isCanUse) {
            if (isCanUse) {
                [self getUserInformationWithAccess_token:access_token openID:openid];
            }else{
                [self useRefresh_token:refresh_tokenNew];
            }
        }];
    } failure:^(NSError * error) {
        NSLog(@"请求失败--%@",error);
    }];
}

#pragma mark - 若未过期,获取用户信息
- (void)getUserInformationWithAccess_token:(NSString *)access_token openID:(NSString *)openID{
    NSString *strOfGetUserInformation = [NSString stringWithFormat:Weixin_GetUserInformation, access_token,openID];
    [HHttpManager GET:strOfGetUserInformation success:^(id responseObject) {
        NSString *nickname = responseObject[@"nickname"];
        NSLog(@"nickname:%@", nickname);
        NSString *headimgurl = responseObject[@"headimgurl"];
        NSLog(@"headimgurl:%@", headimgurl);
                NSNumber *sexnumber = responseObject[@"sex"];
                NSString *sexstr = [NSString stringWithFormat:@"%@",sexnumber];
                NSString *sex;
                if ([sexstr isEqualToString:@"1"]) {
                    sex = @"男";
                }else if ([sexstr isEqualToString:@"2"]){
                    sex = @"女";
                }else{
                    sex = @"未知";
                }
//                NSString *province = responseObject[@"province"];
//                NSString *city = responseObject[@"city"];
//        //用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。
//                    NSString *unionid = responseObject[@"unionid"];


//获取到用户信息后根据需要保存在本地或上传到服务端,用作的用户信息



    } failure:^(NSError * error) {
        NSLog(@"请求失败--%@",error);
    }];
}

#pragma mark - 向后台申请支付请求,获取prepayId
+ (void)sendWeixinPayRequestWithUserId:(NSString *)userId
                               orderId:(NSString *)orderId
                               account:(NSString *)account{
    NSDictionary *paramDict = @{
                                @"userId":@(userId.integerValue),
                                @"orderId":orderId,
                                @"account":@(account.integerValue)
                                };
    //getWeixinPrepayID:后台提供,用来向后台请求prepayId的接口
     [HHttpManager POST:getWeixinPrepayID parameters:paramDict success:^(id responseObject) {
        NSNumber *state = responseObject[@"state"];
        if (state.integerValue == 0) {
            NSString *prepayId = responseObject[@"data"][0][@"prepayId"];
            //获取prepayId后客户端生成签名然后发送支付请求
            [self weixinPayWithPrepayId:prepayId];
        }else{
            
        }
    } failure:^(NSError * error) {
        NSLog(@"请求失败--%@",error);
        
    }];
}

#pragma mark - 调起微信客户端,申请支付
+ (void)weixinPayWithPrepayId:(NSString *)prepayID{
//正常情况下,这里所有的参数都是后台提供给我们,但是我们后台人员不给力,只能我放在客户端来做签名了.希望你们后台人员比较给力.
    PayReq *request = [[PayReq alloc] init];
    //微信支付分配的商户号
    request.partnerId = Weixin_PartnerId;
    //后台给的订单ID
    request.prepayId= prepayID;
    //微信给的固定值,就这么写
    request.package = @"Sign=WXPay";
    //32位随机字符串
    request.nonceStr= [self get32RandomString];
    //时间戳
    request.timeStamp= [self getCurrentTimestamp].intValue;
    /** 
     *签名
     
     *Weixin_AppID:微信开放平台审核通过的应用APPID
     *Weixin_AppKey:在微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 里面设置的微信密钥
     */
    NSString * signStr = [NSString stringWithFormat:@"appid=%@&noncestr=%@&package=%@&partnerid=%@&prepayid=%@&timestamp=%u&key=%@",Weixin_AppID,request.nonceStr,request.package,request.partnerId,request.prepayId,(unsigned int)request.timeStamp,Weixin_AppKey];
    //将签名进行MD5运算并转换为大写
    request.sign= [self stringFromUpperCaseMD5:signStr];
    //支付请求
    [WXApi sendReq:request];
}

#pragma mark - 微信支付是否成功的回调
-(void)handleWeixinPayCallBackResultWithPayResp:(BaseResp *)resp{
    PayResp *response=(PayResp *)resp;
    switch(response.errCode){
        case WXSuccess:
            //服务器端查询支付通知或查询API返回的结果再提示成功
            NSLog(@"支付成功");
            break;
        case WXErrCodeUserCancel:
            NSLog(@"用户点击取消并返回");
            break;
            
        default:
            NSLog(@"支付失败,retcode=%d",resp.errCode);
            /**
             WXErrCodeCommon     = -1,   普通错误类型
             WXErrCodeUserCancel = -2,   用户点击取消并返回
             WXErrCodeSentFail   = -3,   发送失败
             WXErrCodeAuthDeny   = -4,   授权失败
             WXErrCodeUnsupport  = -5,   微信不支持
             */
            
            
            break;
    }
    
}


#pragma mark - 微信分享
//分享到微信好友
+ (void)shareToWeixinFriendWithTitle:(NSString *)title
                             Content:(NSString *)content
                            imageURLStr:(NSString *)urlStr{
    WXMediaMessage *message = [WXMediaMessage message];
    message.title = title;
    message.description = content;
    
    //缩略图
    NSData *thumbData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlStr]];
    UIImage *thumbImage = [UIImage imageWithData:thumbData];
    [message setThumbImage:thumbImage];
    //大图
    WXWebpageObject *webpageObject = [WXWebpageObject object];
    webpageObject.webpageUrl = urlStr;
    message.mediaObject = webpageObject;
    
    SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
    req.bText = NO;
    req.message = message;
    req.scene = WXSceneSession;
    [WXApi sendReq:req];

}


//分享到朋友圈
+ (void)shareToWeixinFriendsCircleWithTitle:(NSString *)title
                             Content:(NSString *)content
                         imageURLStr:(NSString *)urlStr{
    WXMediaMessage *message = [WXMediaMessage message];
    message.title = title;
    message.description = content;
    
    //缩略图
    NSData *thumbData = [NSData dataWithContentsOfURL:[NSURL URLWithString:safeURL(urlStr)]];
    UIImage *thumbImage = [UIImage imageWithData:thumbData];
    [message setThumbImage:thumbImage];
    //大图
    WXWebpageObject *webpageObject = [WXWebpageObject object];
    webpageObject.webpageUrl = safeURL(urlStr);
    message.mediaObject = webpageObject;
    
    SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
    req.bText = NO;
    req.message = message;
    req.scene = WXSceneTimeline;
    [WXApi sendReq:req];
    
}


/** 生成32位随机字符串 */
+(NSString *)get32RandomString{
    
    NSString *string = [[NSString alloc]init];
    for (int i = 0; i < 32; i++) {
        int number = arc4random() % 36;
        if (number < 10) {
            int figure = arc4random() % 10;
            NSString *tempString = [NSString stringWithFormat:@"%d", figure];
            string = [string stringByAppendingString:tempString];
        }else {
            int figure = (arc4random() % 26) + 97;
            char character = figure;
            NSString *tempString = [NSString stringWithFormat:@"%c", character];
            string = [string stringByAppendingString:tempString];
        }
    }
    return string;
    
}

//获取当前时间的时间戳
+(NSString*)getCurrentTimestamp{
    NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];
    NSTimeInterval timestamp=[date timeIntervalSince1970];
    NSString*timeString = [NSString stringWithFormat:@"%0.f", timestamp];
    //    NSLog(@"%@", timeString);
    return timeString;
}

//MD5加密大写
+ (NSString *)stringFromUpperCaseMD5:(NSString *)sourceString{
    const char *string = sourceString.UTF8String;
    unsigned char bytes[CC_MD5_DIGEST_LENGTH];
    extern unsigned char * CC_MD5(const void *data, CC_LONG len, unsigned char *md);
    CC_MD5(string, (CC_LONG)strlen(string), bytes);
    NSMutableString *md5Str = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH];
    for (NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++) {
        [md5Str appendFormat:@"%02x", bytes[count]];
    }
    return [md5Str uppercaseString];
}


@end

设置

在AppDelegate.m文件中初始化

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 //微信
    [HHWeixinSDK initSDK];
}

openURL方法中设置回调(这里一个是iOS9以下的系统调用的系统方法和一个是iOS9以上的系统调用的系统方法,都要设置)

#pragma mark - OpenURL回调结果
//iOS9-
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
    [self handleOpenURLWithURLHost:url];
    return YES;
}
//iOS9和9+
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options{
    [self handleOpenURLWithURLHost:url];
    return YES;
}

- (void)handleOpenURLWithURLHost:(NSURL *)url{
 NSLog(@"url.host:%@", url.host);
   //微信(登录,支付,分享)
   if ([url.host isEqualToString:@"oauth"]||[url.host isEqualToString:@"pay"]||[url.host isEqualToString:@"platformId=wechat"]) {
        [HHWeixinSDK handleOpenURL:url];
    }


}

调用

调用之前需要判断是否安装了微信

/** 
 查看微信是否安装 
 */
+ (BOOL)isWeiXinInstall;

如果上述方法返回YES,直接调用接口就好,个人喜欢类方法,调用方便,可根据喜好自行修改.当然,前提是你已经取得了微信支付和登录分享的授权

/** 
 调用微信登录接口 
 */
+(void)sendWeixinLoginRequest;

/**
 根据订单信息向后台申请prepayId以调用微信支付接口
 
 @param userId   用户id
 @param orderId  订单信息
 @param account  金额
 */
+ (void)sendWeixinPayRequestWithUserId:(NSString *)userId
                               orderId:(NSString *)orderId
                               account:(NSString *)account;

/**
 分享到微信好友
 
 @param title    分享的文字标题
 @param content  分享的文字内容
 @param urlStr   分享的图片URL字符串
 */
+ (void)shareToWeixinFriendWithTitle:(NSString *)title
                             Content:(NSString *)content
                         imageURLStr:(NSString *)urlStr;

/**
 分享到微信朋友圈
 
 @param title    分享的文字标题
 @param content  分享的文字内容
 @param urlStr   分享的图片URL字符串
 */
+ (void)shareToWeixinFriendsCircleWithTitle:(NSString *)title
                                    Content:(NSString *)content
                                imageURLStr:(NSString *)urlStr;

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

推荐阅读更多精彩内容