wechat.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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. callback.call(_this,new Error('Wechat getAccessToken error!'),null);
  38. return;
  39. }
  40. if(body.errcode){
  41. callback(_this,body.errcode,errMsg[body.errcode]);
  42. }
  43. let token = body.access_token;
  44. let expires = body.expires_in;
  45. setTokenToCahe(token,expires);
  46. callback.call(_this,null,token);
  47. })
  48. }
  49. });
  50. },
  51. getJsApiTicket:function(callback){
  52. let _this = this;
  53. Wechat.getAccessToken(function(err,token){
  54. if(!err){
  55. getTicketFromCache(function(data){
  56. if(data){
  57. callback.call(_this,null,data);
  58. }else{
  59. let url = WechatApiURL.getJsApiTicket + token;
  60. Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
  61. if(err || !body){
  62. Logger.error('Wechat getJsApiTicket error!');
  63. Logger.error(err);
  64. callback.call(_this,new Error('Wechat getJsApiTicket error!'),null);
  65. return;
  66. }
  67. if(body.errcode){
  68. callback(_this,body.errcode,errMsg[body.errcode]);
  69. }
  70. let ticket = body.ticket;
  71. let expires = body.expires_in;
  72. setTicketToCahe(ticket,expires);
  73. callback.call(_this,null,ticket);
  74. });
  75. }
  76. });
  77. }else{
  78. Logger.error('Wechat getJsApiTicket error!');
  79. Logger.error(err);
  80. }
  81. });
  82. },
  83. getSignature:function(url,callback){
  84. if(!url) return callback.call(this,new Error('URL is empty!'),null);
  85. let _this = this;
  86. let noncestr = String.random(16);
  87. let timestamp = Math.floor(Moment().valueOf()/1000);
  88. Wechat.getJsApiTicket(function(err,ticket){
  89. if(err){
  90. Logger.error('Wechat getSignature error!');
  91. Logger.error(err);
  92. callback.call(_this,err,null);
  93. return;
  94. }
  95. let combineStr = `jsapi_ticket=${ticket}&noncestr=${noncestr}&timestamp=${timestamp}&url=${url}`;
  96. let signature = sha1(combineStr);
  97. callback.call(_this,null,{nonceStr:noncestr,timestamp:timestamp,signature:signature});
  98. });
  99. },
  100. getJsOauthCodeURL:function(redirectURL,Scope,State){
  101. if(!redirectURL || !redirectURL.match(/(https?):\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/gi)){
  102. return {err:'redirectURL Error'};
  103. }
  104. if(Scope!='snsapi_base' && Scope!='snsapi_userinfo'){
  105. return {err:'Scope must be snsapi_base or snsapi_userinfo'};
  106. }
  107. if(!State) State = 'null';
  108. redirectURL = encodeURI(redirectURL);
  109. let urlString = WechatApiURL.getJsOauthCode + `&redirect_uri=${redirectURL}&response_type=code&scope=${Scope}&state=${State}#wechat_redirect`;
  110. return {err:'',url:urlString};
  111. },
  112. getOauthToken:function(code,callback){
  113. if(!code) return callback.call(this,new Error('Code is empty!'),null);
  114. let _this = this;
  115. let url = WechatApiURL.getOauthToken + `&code=${code}`;
  116. Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
  117. if(err || !body){
  118. Logger.error('Wechat getOauthToken error!');
  119. Logger.error(err);
  120. callback.call(_this,new Error('Wechat getOauthToken error!'),null);
  121. return;
  122. }
  123. if(body.errcode){
  124. return callback.call(_this,body.errcode,errMsg[body.errcode]);
  125. }
  126. callback.call(_this,null,body);
  127. });
  128. },
  129. getOauthUserinfo:function(access_token,openid,callback){
  130. let _this = this;
  131. if(!access_token) return callback.call(this,new Error('access_token is empty'));
  132. if(!openid) return callback.call(this,new Error('openid is empty'));
  133. let url = WechatApiURL.getOauthUserinfo + `&access_token=${access_token}&openid=${openid}`;
  134. Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
  135. if(err || !body){
  136. Logger.error('Wechat getOauthUserinfo error!');
  137. Logger.error(err);
  138. callback.call(_this,new Error('Wechat getOauthUserinfo error!'),null);
  139. return;
  140. }
  141. if(body.errcode){
  142. return callback.call(_this,body.errcode,errMsg[body.errcode]);
  143. }
  144. callback.call(_this,null,body);
  145. });
  146. },
  147. getOauthUserinfoByCode:function(code,callback){
  148. if(!code) return callback.call(this,new Error('Code is empty!'),null);
  149. let _this = this;
  150. Wechat.getOauthToken(code,function(err,result){
  151. if(err) return callback.call(_this,err,null);
  152. Wechat.getOauthUserinfo(result.access_token,result.openid,function(err,result){
  153. if(err) return callback.call(_this,err,null);
  154. callback.call(_this,null,result);
  155. });
  156. });
  157. },
  158. getCode2Session:function(code,callback){
  159. let _this = this;
  160. if(!code) return callback.call(this,new Error('jscode is empty'));
  161. let url = WechatApiURL.getCode2Session + code;
  162. Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
  163. if(err || !body){
  164. Logger.error('Wechat getCode2Session error!');
  165. Logger.error(err);
  166. callback.call(_this,new Error('Wechat getCode2Session error!'),null);
  167. return;
  168. }
  169. if(body.errcode){
  170. return callback.call(_this,body.errcode,errMsg[body.errcode]);
  171. }
  172. callback.call(_this,null,body);
  173. });
  174. }
  175. }
  176. module.exports = Wechat;
  177. function getTokenFromCache(callback){
  178. if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
  179. Memcache.get('wechat_AccessToken',function(err,data){
  180. if(!err && data){
  181. callback(data);
  182. }else{
  183. callback();
  184. }
  185. });
  186. return;
  187. }
  188. try{
  189. FILE.statSync(tmpTokenFile);
  190. }catch(e){
  191. callback(null);
  192. return;
  193. }
  194. let obj = JSON.parse(FILE.readFileSync(tmpTokenFile,'UTF-8'));
  195. let now = Math.floor(Moment().valueOf()/1000)+60;
  196. if(now < obj.expires){
  197. callback(obj.token);
  198. }else{
  199. callback(null);
  200. }
  201. }
  202. function setTokenToCahe(token,expires){
  203. expires = expires - 60;
  204. if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
  205. Memcache.set('wechat_AccessToken',token,expires,function(err){
  206. if(err) console.log(err);
  207. })
  208. return;
  209. }
  210. let expiresTime = Math.floor(Moment().valueOf()/1000) + expires;
  211. FILE.writeFileSync(tmpTokenFile,JSON.stringify({token:token,expires:expiresTime}),'UTF-8');
  212. }
  213. function getTicketFromCache(callback){
  214. if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
  215. Memcache.get('wechat_JsApiTicket',function(err,data){
  216. if(!err && data){
  217. callback(data);
  218. }else{
  219. callback();
  220. }
  221. });
  222. return;
  223. }
  224. try{
  225. FILE.statSync(tmpTicketFile);
  226. }catch(e){
  227. callback(null);
  228. return;
  229. }
  230. let obj = JSON.parse(FILE.readFileSync(tmpTicketFile,'UTF-8'));
  231. let now = Math.floor(Moment().valueOf()/1000)+60;
  232. if(now < obj.expires){
  233. callback(obj.ticket);
  234. }else{
  235. callback(null);
  236. }
  237. }
  238. function setTicketToCahe(ticket,expires){
  239. expires = expires - 5;
  240. if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
  241. Memcache.set('wechat_JsApiTicket',ticket,expires,function(err){
  242. if(err) console.log(err);
  243. })
  244. return;
  245. }
  246. let expiresTime = Math.floor(Moment().valueOf()/1000) + expires;
  247. FILE.writeFileSync(tmpTicketFile,JSON.stringify({ticket:ticket,expires:expiresTime}),'UTF-8');
  248. }