wechat.js 9.2 KB

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