wechat.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /**
  2. * @Author HonorLee (dev@honorlee.me)
  3. * @Version 1.0 (2018-05-05)
  4. * @License MIT
  5. */
  6. 'use strict'
  7. const sha1 = require('sha1');
  8. const tmpTokenFile = Core.Path.Temp + '/wechat_AccessToken.txt';
  9. const tmpTicketFile = Core.Path.Temp + '/wechat_JsApiTicket.txt';
  10. const WechatDomain = `https://${Config.Wechat.apiDomain}`;
  11. const errMsg = require('./errMsg.js');
  12. const WechatApiURL = {
  13. getAccessToken:`${WechatDomain}/cgi-bin/token?grant_type=client_credential&appid=${Config.Wechat.appId}&secret=${Config.Wechat.appSecret}`,
  14. getJsApiTicket:`${WechatDomain}/cgi-bin/ticket/getticket?type=jsapi&access_token=`,
  15. getJsOauthCode:`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${Config.Wechat.appId}`,
  16. getOauthToken:`${WechatDomain}/sns/oauth2/access_token?appid=${Config.Wechat.appId}&secret=${Config.Wechat.appSecret}&grant_type=authorization_code`,
  17. getOauthUserinfo:`${WechatDomain}/sns/userinfo?lang=zh_CN`,
  18. getCode2Session:`${WechatDomain}/sns/jscode2session?appid=${Config.Wechat.appId}&secret=${Config.Wechat.appSecret}&grant_type=authorization_code&js_code=`
  19. }
  20. let Wechat = {
  21. checkSignature:function(signature,timestamp,nonce){
  22. let arr = [Config.Wechat.token, timestamp, nonce];
  23. arr.sort();
  24. let content = arr.join('');
  25. return sha1(content) === signature;
  26. },
  27. getAccessToken:function(callback){
  28. let _this = this;
  29. getTokenFromCache(function(data){
  30. if(data){
  31. callback.call(_this,null,data);
  32. }else{
  33. Core.Request({url:WechatApiURL.getAccessToken,encoding:'UTF-8',json:true},function(err,response,body){
  34. if(err || !body){
  35. LOGGER.error('Wechat getAccessToken error!');
  36. LOGGER.error(err);
  37. console.log(err,body)
  38. callback.call(_this,new Error('Wechat getAccessToken error!'),null);
  39. return;
  40. }
  41. if(body.errcode){
  42. callback(_this,body.errcode,errMsg[body.errcode]);
  43. }
  44. let token = body.access_token;
  45. let expires = body.expires_in;
  46. setTokenToCahe(token,expires);
  47. callback.call(_this,null,token);
  48. })
  49. }
  50. });
  51. },
  52. getJsApiTicket:function(callback){
  53. let _this = this;
  54. Wechat.getAccessToken(function(err,token){
  55. if(!err){
  56. getTicketFromCache(function(data){
  57. if(data){
  58. callback.call(_this,null,data);
  59. }else{
  60. let url = WechatApiURL.getJsApiTicket + token;
  61. Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
  62. if(err || !body){
  63. LOGGER.error('Wechat getJsApiTicket error!');
  64. LOGGER.error(err);
  65. callback.call(_this,new Error('Wechat getJsApiTicket error!'),null);
  66. return;
  67. }
  68. if(body.errcode){
  69. callback(_this,body.errcode,errMsg[body.errcode]);
  70. }
  71. let ticket = body.ticket;
  72. let expires = body.expires_in;
  73. setTicketToCahe(ticket,expires);
  74. callback.call(_this,null,ticket);
  75. });
  76. }
  77. });
  78. }else{
  79. LOGGER.error('Wechat getJsApiTicket error!');
  80. LOGGER.error(err);
  81. }
  82. });
  83. },
  84. getSignature:function(url,callback){
  85. if(!url) return callback.call(this,new Error('URL is empty!'),null);
  86. let _this = this;
  87. let noncestr = String.random(16);
  88. let timestamp = Math.floor(Moment().valueOf()/1000);
  89. Wechat.getJsApiTicket(function(err,ticket){
  90. if(err){
  91. LOGGER.error('Wechat getSignature error!');
  92. LOGGER.error(err);
  93. callback.call(_this,err,null);
  94. return;
  95. }
  96. let combineStr = `jsapi_ticket=${ticket}&noncestr=${noncestr}&timestamp=${timestamp}&url=${url}`;
  97. let signature = sha1(combineStr);
  98. callback.call(_this,null,{nonceStr:noncestr,timestamp:timestamp,signature:signature});
  99. });
  100. },
  101. getJsOauthCodeURL:function(redirectURL,Scope,State){
  102. if(!redirectURL || !redirectURL.match(/(https?):\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/gi)){
  103. return {err:'redirectURL Error'};
  104. }
  105. if(Scope!='snsapi_base' && Scope!='snsapi_userinfo'){
  106. return {err:'Scope must be snsapi_base or snsapi_userinfo'};
  107. }
  108. if(!State) State = 'null';
  109. redirectURL = encodeURI(redirectURL);
  110. let urlString = WechatApiURL.getJsOauthCode + `&redirect_uri=${redirectURL}&response_type=code&scope=${Scope}&state=${State}#wechat_redirect`;
  111. return {err:'',url:urlString};
  112. },
  113. getOauthToken:function(code,callback){
  114. if(!code) return callback.call(this,new Error('Code is empty!'),null);
  115. let _this = this;
  116. let url = WechatApiURL.getOauthToken + `&code=${code}`;
  117. Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
  118. if(err || !body){
  119. LOGGER.error('Wechat getOauthToken error!');
  120. LOGGER.error(err);
  121. callback.call(_this,new Error('Wechat getOauthToken error!'),null);
  122. return;
  123. }
  124. if(body.errcode){
  125. return callback.call(_this,body.errcode,errMsg[body.errcode]);
  126. }
  127. callback.call(_this,null,body);
  128. });
  129. },
  130. getOauthUserinfo:function(access_token,openid,callback){
  131. let _this = this;
  132. if(!access_token) return callback.call(this,new Error('access_token is empty'));
  133. if(!openid) return callback.call(this,new Error('openid is empty'));
  134. let url = WechatApiURL.getOauthUserinfo + `&access_token=${access_token}&openid=${openid}`;
  135. Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
  136. if(err || !body){
  137. LOGGER.error('Wechat getOauthUserinfo error!');
  138. LOGGER.error(err);
  139. callback.call(_this,new Error('Wechat getOauthUserinfo error!'),null);
  140. return;
  141. }
  142. if(body.errcode){
  143. return callback.call(_this,body.errcode,errMsg[body.errcode]);
  144. }
  145. callback.call(_this,null,body);
  146. });
  147. },
  148. getOauthUserinfoByCode:function(code,callback){
  149. if(!code) return callback.call(this,new Error('Code is empty!'),null);
  150. let _this = this;
  151. Wechat.getOauthToken(code,function(err,result){
  152. if(err) return callback.call(_this,err,null);
  153. Wechat.getOauthUserinfo(result.access_token,result.openid,function(err,result){
  154. if(err) return callback.call(_this,err,null);
  155. callback.call(_this,null,result);
  156. });
  157. });
  158. },
  159. getCode2Session:function(code,callback){
  160. let _this = this;
  161. if(!code) return callback.call(this,new Error('jscode is empty'));
  162. let url = WechatApiURL.getCode2Session + code;
  163. Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
  164. if(err || !body){
  165. LOGGER.error('Wechat getCode2Session error!');
  166. LOGGER.error(err);
  167. callback.call(_this,new Error('Wechat getCode2Session error!'),null);
  168. return;
  169. }
  170. if(body.errcode){
  171. return callback.call(_this,body.errcode,errMsg[body.errcode]);
  172. }
  173. callback.call(_this,null,body);
  174. });
  175. }
  176. }
  177. module.exports = Wechat;
  178. function getTokenFromCache(callback){
  179. if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
  180. Memcache.get('Wechat_AccessToken',function(err,data){
  181. if(!err && data){
  182. callback(data);
  183. }else{
  184. callback();
  185. }
  186. });
  187. return;
  188. }
  189. if(Config.Wechat.StoreType=='Redis' && Config.Database.Redis.on){
  190. Redis.hgetall('Wechat_AccessToken',(err,data) => {
  191. if(!err && data){
  192. callback(data.token);
  193. }else{
  194. callback();
  195. }
  196. })
  197. return;
  198. }
  199. try{
  200. FILE.statSync(tmpTokenFile);
  201. }catch(e){
  202. callback(null);
  203. return;
  204. }
  205. let obj = JSON.parse(FILE.readFileSync(tmpTokenFile,'UTF-8'));
  206. let now = Math.floor(Moment().valueOf()/1000)+60;
  207. if(now < obj.expires){
  208. callback(obj.token);
  209. }else{
  210. callback(null);
  211. }
  212. }
  213. function setTokenToCahe(token,expires){
  214. expires = expires - 60;
  215. if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
  216. Memcache.set('Wechat_AccessToken',token,expires,function(err){
  217. if(err) console.log(err);
  218. })
  219. return;
  220. }
  221. if(Config.Wechat.StoreType=='Redis' && Config.Database.Redis.on){
  222. Redis.hmset('Wechat_AccessToken','token',token,function(err){
  223. if(err) console.log(err);
  224. Redis.expire('Wechat_AccessToken',expires);
  225. })
  226. return;
  227. }
  228. let expiresTime = Math.floor(Moment().valueOf()/1000) + expires;
  229. FILE.writeFileSync(tmpTokenFile,JSON.stringify({token:token,expires:expiresTime}),'UTF-8');
  230. }
  231. function getTicketFromCache(callback){
  232. if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
  233. Memcache.get('wechat_JsApiTicket',function(err,data){
  234. if(!err && data){
  235. callback(data);
  236. }else{
  237. callback();
  238. }
  239. });
  240. return;
  241. }
  242. try{
  243. FILE.statSync(tmpTicketFile);
  244. }catch(e){
  245. callback(null);
  246. return;
  247. }
  248. let obj = JSON.parse(FILE.readFileSync(tmpTicketFile,'UTF-8'));
  249. let now = Math.floor(Moment().valueOf()/1000)+60;
  250. if(now < obj.expires){
  251. callback(obj.ticket);
  252. }else{
  253. callback(null);
  254. }
  255. }
  256. function setTicketToCahe(ticket,expires){
  257. expires = expires - 5;
  258. if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
  259. Memcache.set('wechat_JsApiTicket',ticket,expires,function(err){
  260. if(err) console.log(err);
  261. })
  262. return;
  263. }
  264. let expiresTime = Math.floor(Moment().valueOf()/1000) + expires;
  265. FILE.writeFileSync(tmpTicketFile,JSON.stringify({ticket:ticket,expires:expiresTime}),'UTF-8');
  266. }