sdk_mp_redis.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /**
  2. * @Author HonorLee (dev@honorlee.me)
  3. * @Version 2.0 (2019-10-08)
  4. */
  5. // 'use strict'
  6. //基础配置项
  7. import { RedisClientType } from '@redis/client';
  8. import sha1 from 'sha1'
  9. import wxerr from '../errcodes'
  10. // eslint-disable-next-line @typescript-eslint/no-var-requires
  11. // const random = require('string-random')
  12. const errMsg:AnyKeyString = wxerr;
  13. const randomStr = (length?:number,nonum?:boolean)=>{
  14. let arr = '1234567890abcdefghijklmnopqrstuvwxyz';
  15. if(nonum) arr = 'abcdefghijklmnopqrstuvwxyz';
  16. let str = '';
  17. length = length?length:3;
  18. for(let i = 0;i<length;i++){
  19. str += arr.substr(Math.floor(Math.random()*26),1);
  20. }
  21. return str;
  22. }
  23. const WechatDomain = `https://api.weixin.qq.com`;
  24. class WechatSDK{
  25. private _redisClient:RedisClientType;
  26. private _appId;
  27. private _token;
  28. private _appSecret;
  29. private _aesKey;
  30. private _wechatApiURL
  31. public constructor(redisInstance:RedisClientType,options:{appId:string,token:string,appSecret:string,aesKey:string}) {
  32. this._redisClient = redisInstance;
  33. this._appId = options.appId;
  34. this._token = options.token;
  35. this._appSecret = options.appSecret;
  36. this._aesKey = options.aesKey;
  37. this._wechatApiURL = {
  38. getAccessToken:`${WechatDomain}/cgi-bin/token?grant_type=client_credential&appid=${this._appId}&secret=${this._appSecret}`,
  39. getJsApiTicket:`${WechatDomain}/cgi-bin/ticket/getticket?type=jsapi&access_token=`,
  40. getJsOauthCode:`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${this._appId}`,
  41. getOauthToken:`${WechatDomain}/sns/oauth2/access_token?appid=${this._appId}&secret=${this._appSecret}&grant_type=authorization_code`,
  42. getOauthUserinfo:`${WechatDomain}/sns/userinfo?lang=zh_CN`,
  43. getCode2Session:`${WechatDomain}/sns/jscode2session?appid=${this._appId}&secret=${this._appSecret}&grant_type=authorization_code&js_code=`
  44. }
  45. }
  46. //验签
  47. public checkSignature(signature:string,timestamp:string|number,nonce:string){
  48. const arr = [this._token, timestamp, nonce];
  49. arr.sort();
  50. return sha1(arr.join('')) === signature;
  51. }
  52. public getAccessToken(){
  53. return new Promise(async (resolve,reject)=>{
  54. const token = await this._redisClient.get(`WECHAT_${this._appId}_ACCESSTOKEN`);
  55. console.log(!!token)
  56. if(token) return resolve({token});
  57. //请求新的Token
  58. LOGGER.debug('[Wechat] Get new token')
  59. REQUEST({url:this._wechatApiURL.getAccessToken,encoding:'UTF-8',json:true},async (err:any,response:any,body:any)=>{
  60. if(err || !body){
  61. LOGGER.error('Wechat getAccessToken error!');
  62. LOGGER.error(err.stack);
  63. return resolve({err:new Error('Wechat getAccessToken error!')});
  64. }
  65. if(body.errcode){
  66. LOGGER.debug(body.errmsg||'body.errcode');
  67. return resolve({err:new Error(`ERROR_CODE:${errMsg[body.errcode]}`)});
  68. }
  69. const _token = body.access_token;
  70. const expires = body.expires_in - 5 * 60;
  71. await this._redisClient.set(`WECHAT_${this._appId}_ACCESSTOKEN`,_token,{EX:expires});
  72. resolve({token:_token});
  73. });
  74. });
  75. }
  76. public async getJsApiTicket(){
  77. const token:any = await this.getAccessToken();
  78. if(token.err) return token;
  79. return new Promise(async (resolve,reject)=>{
  80. const ticket = await this._redisClient.get(`WECHAT_${this._appId}_JSAPITICKET`);
  81. if(ticket) return resolve({ticket});
  82. REQUEST({url:`${this._wechatApiURL.getJsApiTicket}${token.token}`,encoding:'UTF-8',json:true},(err:any,response:any,body:any)=>{
  83. if(err || !body){
  84. LOGGER.error('Wechat getJsApiTicket error!');
  85. LOGGER.error(err.stack);
  86. return resolve({err:new Error('Wechat getJsApiTicket error!')});
  87. }
  88. if(body.errcode){
  89. return resolve({err:new Error(`ERROR_CODE:${errMsg[body.errcode]}`)});
  90. }
  91. const _ticket = body.ticket;
  92. const expires = body.expires_in - 5;
  93. this._redisClient.set(`WECHAT_${this._appId}_JSAPITICKET`,_ticket,{EX:expires});
  94. resolve({ticket:_ticket});
  95. })
  96. });
  97. }
  98. public async getSignature(url:string){
  99. if(!url) return {err:new Error('URL is empty!')};
  100. const noncestr = randomStr(16);
  101. const timestamp = Math.floor(new Date().getTime());
  102. const ticket = await this.getJsApiTicket();
  103. const combineStr = `jsapi_ticket=${ticket.ticket}&noncestr=${noncestr}&timestamp=${timestamp}&url=${url}`;
  104. const signature = sha1(combineStr);
  105. return {nonceStr:noncestr,timestamp:timestamp,signature:signature,appId:this._appId};
  106. }
  107. public getJsOauthCodeURL(redirectURL:string,Scope:string,State?:string){
  108. if(!redirectURL || !redirectURL.match(/(https?):\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/gi)){
  109. return {err:new Error('redirectURL Error')};
  110. }
  111. if(Scope!='snsapi_base' && Scope!='snsapi_userinfo'){
  112. return {err:new Error('Scope must be snsapi_base or snsapi_userinfo')};
  113. }
  114. if(!State) State = 'null';
  115. redirectURL = encodeURI(redirectURL);
  116. const urlString = `${this._wechatApiURL.getJsOauthCode}&redirect_uri=${redirectURL}&response_type=code&scope=${Scope}&state=${State}#wechat_redirect`;
  117. return {url:urlString};
  118. }
  119. public async getOauthToken(code:string){
  120. if(!code) return {err:new Error('Code is empty!')};
  121. return new Promise((resolve,reject)=>{
  122. REQUEST({url:`${this._wechatApiURL.getOauthToken}&code=${code}`,encoding:'UTF-8',json:true},(err:any,response:any,body:any)=>{
  123. if(err || !body){
  124. LOGGER.error('Wechat getOauthToken error!');
  125. LOGGER.error(err.stack);
  126. return resolve({err:new Error('Wechat getOauthToken error!')});
  127. }
  128. if(body.errcode){
  129. return resolve({err:new Error(`ERROR_CODE:${errMsg[body.errcode]}`)});
  130. }
  131. resolve({token:body.access_token,openid:body.openid});
  132. });
  133. })
  134. }
  135. public getOauthUserinfo(oAuthToken:string,openid:string){
  136. return new Promise((resolve,reject)=>{
  137. REQUEST({url:`${this._wechatApiURL.getOauthUserinfo}&access_token=${oAuthToken}&openid=${openid}`,encoding:'UTF-8',json:true},(err:any,response:any,body:any)=>{
  138. if(err || !body){
  139. LOGGER.error('Wechat getOauthUserinfo error!');
  140. LOGGER.error(err.stack);
  141. return resolve({err:new Error('Wechat getOauthUserinfo error!')});
  142. }
  143. if(body.errcode){
  144. return resolve({err:new Error(`ERROR_CODE:${errMsg[body.errcode]}`)});
  145. }
  146. resolve({userinfo:body});
  147. });
  148. })
  149. }
  150. public async getOauthUserinfoByCode(code:string){
  151. const _authToken = await this.getOauthToken(code) as AnyKeyString;
  152. if(_authToken && !_authToken.err){
  153. const _userInfo = await this.getOauthUserinfo(_authToken.access_token,_authToken.openid);
  154. return _userInfo;
  155. }else{
  156. return null;
  157. }
  158. }
  159. public getCodeSession(code:string){
  160. return new Promise((resolve,reject)=>{
  161. REQUEST({url:`${this._wechatApiURL.getCode2Session}${code}`,encoding:'UTF-8',json:true},(err:any,response:any,body:any)=>{
  162. // console.log(1,err,response,body)
  163. if(err || !body){
  164. LOGGER.error('Wechat getCode2Session error!');
  165. LOGGER.error(err.stack);
  166. return resolve({err:new Error('Wechat getCode2Session error!')});
  167. }
  168. if(body.errcode){
  169. return resolve({err:new Error(`ERROR_CODE:${body.errcode} - ${errMsg[body.errcode]}`)});
  170. }
  171. resolve({openid:body.openid,unionid:body.unionid,session_key:body.session_key});
  172. });
  173. })
  174. }
  175. }
  176. module.exports = WechatSDK;