wechatPublic
This commit is contained in:
parent
c46a5bbf93
commit
951e640164
|
|
@ -1,19 +1,23 @@
|
|||
import { WechatMpInstance } from './service/wechat/WechatMp';
|
||||
import { WechatPublicInstance } from './service/wechat/WechatPublic';
|
||||
|
||||
class WechatSDK {
|
||||
mpMap: Record<string, WechatMpInstance>;
|
||||
publicMap: Record<string, any>;
|
||||
webMap: Record<string, any>;
|
||||
|
||||
constructor() {
|
||||
this.mpMap = {};
|
||||
this.publicMap = {};
|
||||
this.webMap = {};
|
||||
}
|
||||
|
||||
getInstance(
|
||||
appId: string,
|
||||
appSecret: string,
|
||||
type: 'wechatMp' | 'wechatPublic'
|
||||
type: 'wechatMp' | 'wechatPublic' | 'web'
|
||||
) {
|
||||
// type 支持web网站扫码登录
|
||||
if (type === 'wechatMp') {
|
||||
if (this.mpMap[appId]) {
|
||||
return this.mpMap[appId];
|
||||
|
|
@ -23,6 +27,15 @@ class WechatSDK {
|
|||
[appId]: instance,
|
||||
});
|
||||
return instance;
|
||||
} else if (type === 'wechatPublic') {
|
||||
if (this.publicMap[appId]) {
|
||||
return this.publicMap[appId];
|
||||
}
|
||||
const instance = new WechatPublicInstance(appId, appSecret);
|
||||
Object.assign(this.publicMap, {
|
||||
[appId]: instance,
|
||||
});
|
||||
return instance;
|
||||
} else {
|
||||
throw new Error('public not implemented');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export class WechatMpInstance {
|
|||
appSecret: string;
|
||||
|
||||
accessToken?: string;
|
||||
refreshAcessTokenHandler?: any;
|
||||
refreshAccessTokenHandler?: any;
|
||||
|
||||
constructor(appId: string, appSecret: string) {
|
||||
this.appId = appId;
|
||||
|
|
@ -61,7 +61,7 @@ export class WechatMpInstance {
|
|||
const { access_token, expires_in } = result;
|
||||
this.accessToken = access_token;
|
||||
// 生成下次刷新的定时器
|
||||
this.refreshAcessTokenHandler = setTimeout(() => {
|
||||
this.refreshAccessTokenHandler = setTimeout(() => {
|
||||
this.refreshAccessToken();
|
||||
}, (expires_in - 10) * 1000);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
import crypto from 'crypto';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
export class WechatPublicInstance {
|
||||
appId: string;
|
||||
appSecret: string;
|
||||
|
||||
accessToken?: string;
|
||||
refreshAccessTokenHandler?: any;
|
||||
|
||||
constructor(appId: string, appSecret: string) {
|
||||
this.appId = appId;
|
||||
this.appSecret = appSecret;
|
||||
|
||||
this.refreshAccessToken();
|
||||
}
|
||||
|
||||
private async access(url: string, init?: RequestInit) {
|
||||
const response = await fetch(url, init);
|
||||
|
||||
const { headers, status } = response;
|
||||
if (![200, 201].includes(status)) {
|
||||
throw new Error(`微信服务器返回不正确应答:${status}`);
|
||||
}
|
||||
const contentType = (headers as any)['Content-Type'] || headers.get('Content-Type')!;
|
||||
if (contentType.includes('application/json')) {
|
||||
const json = await response.json();
|
||||
if (typeof json.errcode === 'number' && json.errcode !== 0) {
|
||||
throw new Error(`调用微信接口返回出错,code是${json.errcode},信息是${json.errmsg}`);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
if (
|
||||
contentType.includes('text')
|
||||
|| contentType.includes('xml')
|
||||
|| contentType.includes('html')
|
||||
) {
|
||||
const data = await response.text();
|
||||
return data;
|
||||
}
|
||||
if (contentType.includes('application/octet-stream')) {
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async code2Session(code: string) {
|
||||
const result = await this.access(`https://api.weixin.qq.com/sns/oauth2/access_token?appid=${this.appId}&secret=${this.appSecret}&code=${code}&grant_type=authorization_code`);
|
||||
const { session_key, openid, unionid } = JSON.parse(result); // 这里微信返回的数据竟然是text/plain
|
||||
|
||||
return {
|
||||
sessionKey: session_key as string,
|
||||
openId: openid as string,
|
||||
unionId: unionid as string,
|
||||
};
|
||||
}
|
||||
|
||||
private async refreshAccessToken() {
|
||||
const result = await this.access(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.appId}&secret=${this.appSecret}`);
|
||||
const { access_token, expires_in } = result;
|
||||
this.accessToken = access_token;
|
||||
// 生成下次刷新的定时器
|
||||
this.refreshAccessTokenHandler = setTimeout(() => {
|
||||
this.refreshAccessToken();
|
||||
}, (expires_in - 10) * 1000);
|
||||
}
|
||||
|
||||
|
||||
decryptData(sessionKey: string, encryptedData: string, iv: string, signature: string) {
|
||||
const skBuf = Buffer.from(sessionKey, 'base64');
|
||||
// const edBuf = Buffer.from(encryptedData, 'base64');
|
||||
const ivBuf = Buffer.from(iv, 'base64');
|
||||
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes-128-cbc', skBuf, ivBuf);
|
||||
// 设置自动 padding 为 true,删除填充补位
|
||||
decipher.setAutoPadding(true);
|
||||
let decoded = decipher.update(encryptedData, 'base64', 'utf8');
|
||||
decoded += decipher.final('utf8');
|
||||
|
||||
const data = JSON.parse(decoded);
|
||||
|
||||
if (data.watermark.appid !== this.appId) {
|
||||
throw new Error('Illegal Buffer');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue