/** 转载请保留本人博客园的原地址以及版权声明,请勿恶意修改
* 作者:杨浩瑞 QQ:1420213383
* 演示系统地址:【后台】http://xiaoshuo.qqsiot.cn/manager 【前台】http://y6.qqsiot.cn
* 管理员账号:admin 渠道商账号:channel 代理商账号:agent 演示密码:1413051356 **/
一、微信小说分销系统是什么?
微信小说分销系统是一种合法变现系统,通过购买小说,添加到系统中,然后寻找一些需要粉丝变现的公众号管理者来做自己的渠道商,然后读者阅读小说,充值的钱80%-90%直接给渠道商。平台收取10%-20%,当然,比例自己可以设置。
二、为什么要多网页授权登录
对于微信小说分销系统来说,必然会出现多个公众号的情况,其实就是多个渠道商,每个渠道商都拥有一个公众号,为了更好的隔离渠道商,设计每个渠道商分配一个专属子域名,比如y{0}.qqsiot.cn。
为了更好的用户体验,需要实现自动登录和识别用户的功能,这样读者打开网站的时候不需要输入账号和密码之类的信息来验证信息,除了自动登录,还可以方便后期付款的时候可以调起微信支付。
三、多公众号设计方案
既然考虑到用户体验,不想让验证账号密码,减少繁琐的步骤,那么直接使用微信的无感授权登录方式,snsapi_base方式可以在用户没有关注公众号的时候获取到该用户的openid,并且不需要谈起授权页面,对于用户来说,整个过程是无感的,但是不好的地方在于,此方法无法获取到用户的详细信息,比如昵称、性别、年龄等(不方便后期分析网站的用户喜好和分布)。还有一种方式:snsapi_userinfo,此方式如果是从公众号内部进入的话是没有授权弹窗的,但是如果用户没有关注公众号,此时会弹出是否授权的页面,如果用户拒绝授权,那势必会造成一些不好的后果。
虽然snsapi_base方式无法获取到用户详细信息,但是可以考虑使用其他的办法解决,两个地方可以处理:
1、当用户阅读到一定章节的时候,提示关注公众号,关注成功后,微信会向我们的服务器推送一条关注消息,此时通过微信的获取用户消息接口去获取该用户的信息
收到的关注推送如下:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[subscribe]]></Event> </xml>
2、每次阅读时需要判断是否关注,若用户未关注,则提示关注,若已关注,并且数据库中记录是未关注,那么更新一下用户的详细信息
上边两个地方都提到了获取用户详细信息的接口,接口地址为:https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
可以获取到如下信息:
{ "subscribe": 1, "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", "nickname": "Band", "sex": 1, "language": "zh_CN", "city": "广州", "province": "广东", "country": "中国", "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4 eMsv84eavHiaiceqxibJxCfHe/0", "subscribe_time": 1382694957, "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" "remark": "", "groupid": 0, "tagid_list":[128,2] }
有点跑题了,言归正传.
四、后台配置基本的公众号信息
每个渠道商可以后台设置自己的公众号的微信号、AppId、AppSecret,然后再微信公众号后台配置授权域名等信息,如下图:
微信后台需要配置的信息如下图:
五、源码分析
这些配置好之后,在数据库中保存这些信息,当用户打开网站的时候,拿出这些信息去授权。
var isWechat = Request.UserAgent.Contains("MicroMessenger"); if (isWechat) { int wid = Common.GetHomeWid(); string url = Request.Url.Scheme + "://" + Request.Url.Host; //传递参数,获取用户信息后,可跳转到自己定义的页面,想怎么处理就怎么处理 reurl = reurl ?? url; string appId; //只要打开这个付款页面,就开始付款,不需要判断什么了 Uri reurlObj = new Uri(reurl); if (reurlObj.LocalPath.ToLower().StartsWith("/payment/pay")) { appId = WxManFactory.AppId; } else { var account = _accountBll.Get(wid); if (account == null) { return RedirectToAction("Subscribe"); } appId = account.WechatPublicAppId; } reurl = Server.UrlEncode(reurl); var wxreurl = Server.UrlEncode(url + "/User/WechatLogin?reurl=" + reurl); //CommonBll.WriteLogFile("Login->" + wxreurl); //弹出授权页面(如在不弹出授权页面基础下未获得openid,弹出授权页面,提示用户授权) if (!string.IsNullOrWhiteSpace(auth) && auth == "1") { return Redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appId + "&redirect_uri=" + wxreurl + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect"); } //不弹出授权页面 return Redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appId + "&redirect_uri=" + wxreurl + "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"); } return RedirectToAction("Subscribe");
不要一味地去授权,先判断一下是不是在微信浏览器打开的,如果是的话才会去授权,如果不是的话,跳转到关注页面,此页面也是自己写的,上边有公众号的二维码,方便用户使用手机微信扫描关注。
如果是微信端,那么先获取渠道商的AppId,并且记录授权后需要跳转的地址,甚至是获取code和openid后再次跳转的地址。
if (!string.IsNullOrWhiteSpace(code)) { string url = Request.Url.Scheme + "://" + Request.Url.Host; reurl = reurl ?? url; Uri reurlObj = new Uri(reurl); //只要打开这个付款页面,就开始付款,不需要判断什么了 if (reurlObj.LocalPath.ToLower().StartsWith("/payment/pay")) { var model1 = WxManFactory.Get_token(code); if (model1 == null || string.IsNullOrWhiteSpace(model1.openid)) { return RedirectToAction("PayConfigError"); } Session["openid"] = model1.openid; return Redirect(reurl); } int wid = Common.GetHomeWid(); NameValueCollection collection = HttpUtility.ParseQueryString(reurlObj.Query); var wexinTool = new WeixinNormalTool(wid); OAuthToken model = wexinTool.Get_token(code); //获取token //只做基本授权即可 UserBll userBll = new UserBll(); var user = userBll.GetByOpenId(model.openid); if (user == null) { //说明没注册 UserReferralBll urBll = new UserReferralBll(); ConfigBll configBll = new ConfigBll(); //其他平台ID var otherPlatform = collection.Get("other_id"); user = new User { Openid = model.openid, CoinNum = configBll.GetFirstLoginCoin(), Sex = 0,//1男2女0未知 BelongToChanneltId = wid, BelongToAgentId = wid, OtherPlatform = otherPlatform??"" }; //推广链接ID var referralId = collection.Get("referralId"); var ur = urBll.Get(referralId); int agentId; var uid = collection.Get("uid"); if (ur != null) { //record belong to user.BelongToAgentId = ur.AgentId; user.BecauseReferralId = int.Parse(referralId); } else if (int.TryParse(uid, out agentId)) { //不是第一种推广链接的时候,判断是否是第二种 var account = _accountBll.Get(agentId); if (account.CreateBy == wid)//只有这个代理的id确实属于该渠道的时候,才会加到该渠道 { user.BelongToAgentId = agentId; } } //为用户注册账号 userBll.Add(user); } Session["openid"] = model.openid; //跳转到自己的页面,想怎么处理就怎么处理 return Redirect(reurl); } return RedirectToAction("Subscribe");
此时授权成功,并且如果用户没有注册会自动注册,如果已经注册了,则直接打开刚才被拦截的页面,另外代码里边还写了支付页面的拦截,以此实现支付独立的功能。
后台手机查看,完美匹配