import { fetch } from "undici" export interface WeixinAccessTokenResult { accessToken: string expiresAt: number refreshToken: string openid: string scope: string unionid?: string | undefined } export interface WeixinUserInfoResult { openid: string nickname: string sex: number province: string city: string country: string headimgurl: string privilege: string[] unionid?: string | undefined } export interface WeixinOauth2Options { appId: string appSecret: string } export class WeixinOauth2 { protected options: WeixinOauth2Options constructor(options: WeixinOauth2Options) { this.options = options } /** * @description {@link https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html | 关于微信快速登录功能的说明} */ async getAccessToken(code: string): Promise { interface AccessTokenSuccessResponse { access_token: string expires_in: number refresh_token: string openid: string scope: string unionid?: string } interface AccessTokenErrorResponse { errcode: number errmsg: string } const { appId, appSecret } = this.options const url = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appId}&secret=${appSecret}&code=${code}&grant_type=authorization_code` try { const now = Date.now() const result = await fetch(url, { method: "get", }) const json = (await result.json()) as AccessTokenSuccessResponse | AccessTokenErrorResponse if ("errcode" in json) { throw new Error(`${json.errcode}: ${json.errmsg}`) } return { accessToken: json.access_token, expiresAt: now + json.expires_in * 1_000, refreshToken: json.refresh_token, openid: json.openid, scope: json.scope, unionid: json.unionid, } } catch (exception) { console.error("Error getting access token:", exception) throw exception } } /** * @description {@link https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Authorized_Interface_Calling_UnionID.html | 获取用户个人信息(UnionID机制)} */ async getUserInfo(accessToken: string, openId: string): Promise { interface UserInfoSuccessResponse { openid: string nickname: string sex: number province: string city: string country: string headimgurl: string privilege: string[] unionid?: string } interface UserInfoErrorResponse { errcode: number errmsg: string } const url = `https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openId}&lang=zh_CN` try { const result = await fetch(url, { method: "get", }) const json = (await result.json()) as UserInfoSuccessResponse | UserInfoErrorResponse if ("errcode" in json) { throw new Error(`${json.errcode}: ${json.errmsg}`) } return json } catch (exception) { console.error("Error getting user info:", exception) throw exception } } }