/** * @Author HonorLee (dev@honorlee.me) * @Version 1.0 (2018-05-05) * @License MIT */ 'use strict' const sha1 = require('sha1'); const tmpTokenFile = Core.Path.Temp + '/wechat_AccessToken.txt'; const tmpTicketFile = Core.Path.Temp + '/wechat_JsApiTicket.txt'; const WechatDomain = `https://${Config.Wechat.apiDomain}`; const errMsg = require('./errMsg.js'); const WechatApiURL = { getAccessToken:`${WechatDomain}/cgi-bin/token?grant_type=client_credential&appid=${Config.Wechat.appId}&secret=${Config.Wechat.appSecret}`, getJsApiTicket:`${WechatDomain}/cgi-bin/ticket/getticket?type=jsapi&access_token=`, getJsOauthCode:`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${Config.Wechat.appId}`, getOauthToken:`${WechatDomain}/sns/oauth2/access_token?appid=${Config.Wechat.appId}&secret=${Config.Wechat.appSecret}&grant_type=authorization_code`, getOauthUserinfo:`${WechatDomain}/sns/userinfo?lang=zh_CN`, getCode2Session:`${WechatDomain}/sns/jscode2session?appid=${Config.Wechat.appId}&secret=${Config.Wechat.appSecret}&grant_type=authorization_code&js_code=` } let Wechat = { checkSignature:function(signature,timestamp,nonce){ let arr = [Config.Wechat.token, timestamp, nonce]; arr.sort(); let content = arr.join(''); return sha1(content) === signature; }, getAccessToken:function(callback){ let _this = this; getTokenFromCache(function(data){ if(data){ callback.call(_this,null,data); }else{ Core.Request({url:WechatApiURL.getAccessToken,encoding:'UTF-8',json:true},function(err,response,body){ if(err || !body){ Logger.error('Wechat getAccessToken error!'); Logger.error(err); callback.call(_this,new Error('Wechat getAccessToken error!'),null); return; } if(body.errcode){ callback(_this,body.errcode,errMsg[body.errcode]); } let token = body.access_token; let expires = body.expires_in; setTokenToCahe(token,expires); callback.call(_this,null,token); }) } }); }, getJsApiTicket:function(callback){ let _this = this; Wechat.getAccessToken(function(err,token){ if(!err){ getTicketFromCache(function(data){ if(data){ callback.call(_this,null,data); }else{ let url = WechatApiURL.getJsApiTicket + token; Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){ if(err || !body){ Logger.error('Wechat getJsApiTicket error!'); Logger.error(err); callback.call(_this,new Error('Wechat getJsApiTicket error!'),null); return; } if(body.errcode){ callback(_this,body.errcode,errMsg[body.errcode]); } let ticket = body.ticket; let expires = body.expires_in; setTicketToCahe(ticket,expires); callback.call(_this,null,ticket); }); } }); }else{ Logger.error('Wechat getJsApiTicket error!'); Logger.error(err); } }); }, getSignature:function(url,callback){ if(!url) return callback.call(this,new Error('URL is empty!'),null); let _this = this; let noncestr = String.random(16); let timestamp = Math.floor(Moment().valueOf()/1000); Wechat.getJsApiTicket(function(err,ticket){ if(err){ Logger.error('Wechat getSignature error!'); Logger.error(err); callback.call(_this,err,null); return; } let combineStr = `jsapi_ticket=${ticket}&noncestr=${noncestr}×tamp=${timestamp}&url=${url}`; let signature = sha1(combineStr); callback.call(_this,null,{nonceStr:noncestr,timestamp:timestamp,signature:signature}); }); }, getJsOauthCodeURL:function(redirectURL,Scope,State){ if(!redirectURL || !redirectURL.match(/(https?):\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/gi)){ return {err:'redirectURL Error'}; } if(Scope!='snsapi_base' && Scope!='snsapi_userinfo'){ return {err:'Scope must be snsapi_base or snsapi_userinfo'}; } if(!State) State = 'null'; redirectURL = encodeURI(redirectURL); let urlString = WechatApiURL.getJsOauthCode + `&redirect_uri=${redirectURL}&response_type=code&scope=${Scope}&state=${State}#wechat_redirect`; return {err:'',url:urlString}; }, getOauthToken:function(code,callback){ if(!code) return callback.call(this,new Error('Code is empty!'),null); let _this = this; let url = WechatApiURL.getOauthToken + `&code=${code}`; Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){ if(err || !body){ Logger.error('Wechat getOauthToken error!'); Logger.error(err); callback.call(_this,new Error('Wechat getOauthToken error!'),null); return; } if(body.errcode){ return callback.call(_this,body.errcode,errMsg[body.errcode]); } callback.call(_this,null,body); }); }, getOauthUserinfo:function(access_token,openid,callback){ let _this = this; if(!access_token) return callback.call(this,new Error('access_token is empty')); if(!openid) return callback.call(this,new Error('openid is empty')); let url = WechatApiURL.getOauthUserinfo + `&access_token=${access_token}&openid=${openid}`; Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){ if(err || !body){ Logger.error('Wechat getOauthUserinfo error!'); Logger.error(err); callback.call(_this,new Error('Wechat getOauthUserinfo error!'),null); return; } if(body.errcode){ return callback.call(_this,body.errcode,errMsg[body.errcode]); } callback.call(_this,null,body); }); }, getOauthUserinfoByCode:function(code,callback){ if(!code) return callback.call(this,new Error('Code is empty!'),null); let _this = this; Wechat.getOauthToken(code,function(err,result){ if(err) return callback.call(_this,err,null); Wechat.getOauthUserinfo(result.access_token,result.openid,function(err,result){ if(err) return callback.call(_this,err,null); callback.call(_this,null,result); }); }); }, getCode2Session:function(code,callback){ let _this = this; if(!code) return callback.call(this,new Error('jscode is empty')); let url = WechatApiURL.getCode2Session + code; Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){ if(err || !body){ Logger.error('Wechat getCode2Session error!'); Logger.error(err); callback.call(_this,new Error('Wechat getCode2Session error!'),null); return; } if(body.errcode){ return callback.call(_this,body.errcode,errMsg[body.errcode]); } callback.call(_this,null,body); }); } } module.exports = Wechat; function getTokenFromCache(callback){ if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){ Memcache.get('wechat_AccessToken',function(err,data){ if(!err && data){ callback(data); }else{ callback(); } }); return; } try{ FILE.statSync(tmpTokenFile); }catch(e){ callback(null); return; } let obj = JSON.parse(FILE.readFileSync(tmpTokenFile,'UTF-8')); let now = Math.floor(Moment().valueOf()/1000)+60; if(now < obj.expires){ callback(obj.token); }else{ callback(null); } } function setTokenToCahe(token,expires){ expires = expires - 60; if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){ Memcache.set('wechat_AccessToken',token,expires,function(err){ if(err) console.log(err); }) return; } let expiresTime = Math.floor(Moment().valueOf()/1000) + expires; FILE.writeFileSync(tmpTokenFile,JSON.stringify({token:token,expires:expiresTime}),'UTF-8'); } function getTicketFromCache(callback){ if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){ Memcache.get('wechat_JsApiTicket',function(err,data){ if(!err && data){ callback(data); }else{ callback(); } }); return; } try{ FILE.statSync(tmpTicketFile); }catch(e){ callback(null); return; } let obj = JSON.parse(FILE.readFileSync(tmpTicketFile,'UTF-8')); let now = Math.floor(Moment().valueOf()/1000)+60; if(now < obj.expires){ callback(obj.ticket); }else{ callback(null); } } function setTicketToCahe(ticket,expires){ expires = expires - 5; if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){ Memcache.set('wechat_JsApiTicket',ticket,expires,function(err){ if(err) console.log(err); }) return; } let expiresTime = Math.floor(Moment().valueOf()/1000) + expires; FILE.writeFileSync(tmpTicketFile,JSON.stringify({ticket:ticket,expires:expiresTime}),'UTF-8'); }