accessToken.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. // Package context 开放平台相关context
  2. package context
  3. import (
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "net/url"
  8. "time"
  9. "github.com/silenceper/wechat/v2/cache"
  10. "github.com/silenceper/wechat/v2/util"
  11. )
  12. const (
  13. componentAccessTokenURL = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"
  14. getPreCodeURL = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=%s"
  15. queryAuthURL = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=%s"
  16. refreshTokenURL = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=%s"
  17. getComponentInfoURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%s"
  18. componentLoginURL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=%d&biz_appid=%s"
  19. bindComponentURL = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=%d&no_scan=1&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&biz_appid=%s#wechat_redirect"
  20. bindComponentURLV2 = "https://open.weixin.qq.com/wxaopen/safe/bindcomponent?action=bindcomponent&auth_type=%d&no_scan=1&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&biz_appid=%s#wechat_redirect"
  21. // TODO 获取授权方选项信息
  22. // getComponentConfigURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=%s"
  23. // TODO 获取已授权的账号信息
  24. // getuthorizerListURL = "POST https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=%s"
  25. )
  26. // ComponentAccessToken 第三方平台
  27. type ComponentAccessToken struct {
  28. util.CommonError
  29. AccessToken string `json:"component_access_token"`
  30. ExpiresIn int64 `json:"expires_in"`
  31. }
  32. // GetComponentAccessTokenContext 获取 ComponentAccessToken
  33. func (ctx *Context) GetComponentAccessTokenContext(stdCtx context.Context) (string, error) {
  34. accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
  35. val := cache.GetContext(stdCtx, ctx.Cache, accessTokenCacheKey)
  36. if val == nil {
  37. return "", fmt.Errorf("cann't get component access token")
  38. }
  39. return val.(string), nil
  40. }
  41. // GetComponentAccessToken 获取 ComponentAccessToken
  42. func (ctx *Context) GetComponentAccessToken() (string, error) {
  43. return ctx.GetComponentAccessTokenContext(context.Background())
  44. }
  45. // SetComponentAccessTokenContext 通过component_verify_ticket 获取 ComponentAccessToken
  46. func (ctx *Context) SetComponentAccessTokenContext(stdCtx context.Context, verifyTicket string) (*ComponentAccessToken, error) {
  47. body := map[string]string{
  48. "component_appid": ctx.AppID,
  49. "component_appsecret": ctx.AppSecret,
  50. "component_verify_ticket": verifyTicket,
  51. }
  52. respBody, err := util.PostJSONContext(stdCtx, componentAccessTokenURL, body)
  53. if err != nil {
  54. return nil, err
  55. }
  56. at := &ComponentAccessToken{}
  57. if err := json.Unmarshal(respBody, at); err != nil {
  58. return nil, err
  59. }
  60. if at.ErrCode != 0 {
  61. return nil, fmt.Errorf("SetComponentAccessToken Error , errcode=%d , errmsg=%s", at.ErrCode, at.ErrMsg)
  62. }
  63. accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
  64. expires := at.ExpiresIn - 1500
  65. if err := cache.SetContext(stdCtx, ctx.Cache, accessTokenCacheKey, at.AccessToken, time.Duration(expires)*time.Second); err != nil {
  66. return nil, nil
  67. }
  68. return at, nil
  69. }
  70. // SetComponentAccessToken 通过component_verify_ticket 获取 ComponentAccessToken
  71. func (ctx *Context) SetComponentAccessToken(stdCtx context.Context, verifyTicket string) (*ComponentAccessToken, error) {
  72. return ctx.SetComponentAccessTokenContext(stdCtx, verifyTicket)
  73. }
  74. // GetPreCodeContext 获取预授权码
  75. func (ctx *Context) GetPreCodeContext(stdCtx context.Context) (string, error) {
  76. cat, err := ctx.GetComponentAccessTokenContext(stdCtx)
  77. if err != nil {
  78. return "", err
  79. }
  80. req := map[string]string{
  81. "component_appid": ctx.AppID,
  82. }
  83. uri := fmt.Sprintf(getPreCodeURL, cat)
  84. body, err := util.PostJSONContext(stdCtx, uri, req)
  85. if err != nil {
  86. return "", err
  87. }
  88. var ret struct {
  89. PreCode string `json:"pre_auth_code"`
  90. }
  91. err = json.Unmarshal(body, &ret)
  92. return ret.PreCode, err
  93. }
  94. // GetPreCode 获取预授权码
  95. func (ctx *Context) GetPreCode() (string, error) {
  96. return ctx.GetPreCodeContext(context.Background())
  97. }
  98. // GetComponentLoginPageContext 获取第三方公众号授权链接(扫码授权)
  99. func (ctx *Context) GetComponentLoginPageContext(stdCtx context.Context, redirectURI string, authType int, bizAppID string) (string, error) {
  100. code, err := ctx.GetPreCodeContext(stdCtx)
  101. if err != nil {
  102. return "", err
  103. }
  104. return fmt.Sprintf(componentLoginURL, ctx.AppID, code, url.QueryEscape(redirectURI), authType, bizAppID), nil
  105. }
  106. // GetComponentLoginPage 获取第三方公众号授权链接(扫码授权)
  107. func (ctx *Context) GetComponentLoginPage(redirectURI string, authType int, bizAppID string) (string, error) {
  108. return ctx.GetComponentLoginPageContext(context.Background(), redirectURI, authType, bizAppID)
  109. }
  110. // GetBindComponentURLContext 获取第三方公众号授权链接(链接跳转,适用移动端)
  111. func (ctx *Context) GetBindComponentURLContext(stdCtx context.Context, redirectURI string, authType int, bizAppID string) (string, error) {
  112. code, err := ctx.GetPreCodeContext(stdCtx)
  113. if err != nil {
  114. return "", err
  115. }
  116. return fmt.Sprintf(bindComponentURL, authType, ctx.AppID, code, url.QueryEscape(redirectURI), bizAppID), nil
  117. }
  118. // GetBindComponentURL 获取第三方公众号授权链接(链接跳转,适用移动端)
  119. func (ctx *Context) GetBindComponentURL(redirectURI string, authType int, bizAppID string) (string, error) {
  120. return ctx.GetBindComponentURLContext(context.Background(), redirectURI, authType, bizAppID)
  121. }
  122. // GetBindComponentURLV2Context 获取新版本第三方公众号授权链接(链接跳转,适用移动端)
  123. func (ctx *Context) GetBindComponentURLV2Context(stdCtx context.Context, redirectURI string, authType int, bizAppID string) (string, error) {
  124. code, err := ctx.GetPreCodeContext(stdCtx)
  125. if err != nil {
  126. return "", err
  127. }
  128. return fmt.Sprintf(bindComponentURLV2, authType, ctx.AppID, code, url.QueryEscape(redirectURI), bizAppID), nil
  129. }
  130. // GetBindComponentURLV2 获取新版本第三方公众号授权链接(链接跳转,适用移动端)
  131. func (ctx *Context) GetBindComponentURLV2(redirectURI string, authType int, bizAppID string) (string, error) {
  132. return ctx.GetBindComponentURLContext(context.Background(), redirectURI, authType, bizAppID)
  133. }
  134. // ID 微信返回接口中各种类型字段
  135. type ID struct {
  136. ID int `json:"id"`
  137. }
  138. // AuthBaseInfo 授权的基本信息
  139. type AuthBaseInfo struct {
  140. AuthrAccessToken
  141. FuncInfo []AuthFuncInfo `json:"func_info"`
  142. }
  143. // AuthFuncInfo 授权的接口内容
  144. type AuthFuncInfo struct {
  145. FuncscopeCategory ID `json:"funcscope_category"`
  146. }
  147. // AuthrAccessToken 授权方AccessToken
  148. type AuthrAccessToken struct {
  149. Appid string `json:"authorizer_appid"`
  150. AccessToken string `json:"authorizer_access_token"`
  151. ExpiresIn int64 `json:"expires_in"`
  152. RefreshToken string `json:"authorizer_refresh_token"`
  153. }
  154. // QueryAuthCodeContext 使用授权码换取公众号或小程序的接口调用凭据和授权信息
  155. func (ctx *Context) QueryAuthCodeContext(stdCtx context.Context, authCode string) (*AuthBaseInfo, error) {
  156. cat, err := ctx.GetComponentAccessTokenContext(stdCtx)
  157. if err != nil {
  158. return nil, err
  159. }
  160. req := map[string]string{
  161. "component_appid": ctx.AppID,
  162. "authorization_code": authCode,
  163. }
  164. uri := fmt.Sprintf(queryAuthURL, cat)
  165. body, err := util.PostJSONContext(stdCtx, uri, req)
  166. if err != nil {
  167. return nil, err
  168. }
  169. var ret struct {
  170. util.CommonError
  171. Info *AuthBaseInfo `json:"authorization_info"`
  172. }
  173. if err := json.Unmarshal(body, &ret); err != nil {
  174. return nil, err
  175. }
  176. if ret.ErrCode != 0 {
  177. err = fmt.Errorf("QueryAuthCode error : errcode=%v , errmsg=%v", ret.ErrCode, ret.ErrMsg)
  178. return nil, err
  179. }
  180. return ret.Info, nil
  181. }
  182. // QueryAuthCode 使用授权码换取公众号或小程序的接口调用凭据和授权信息
  183. func (ctx *Context) QueryAuthCode(authCode string) (*AuthBaseInfo, error) {
  184. return ctx.QueryAuthCodeContext(context.Background(), authCode)
  185. }
  186. // RefreshAuthrTokenContext 获取(刷新)授权公众号或小程序的接口调用凭据(令牌)
  187. func (ctx *Context) RefreshAuthrTokenContext(stdCtx context.Context, appid, refreshToken string) (*AuthrAccessToken, error) {
  188. cat, err := ctx.GetComponentAccessTokenContext(stdCtx)
  189. if err != nil {
  190. return nil, err
  191. }
  192. req := map[string]string{
  193. "component_appid": ctx.AppID,
  194. "authorizer_appid": appid,
  195. "authorizer_refresh_token": refreshToken,
  196. }
  197. uri := fmt.Sprintf(refreshTokenURL, cat)
  198. body, err := util.PostJSONContext(stdCtx, uri, req)
  199. if err != nil {
  200. return nil, err
  201. }
  202. ret := &AuthrAccessToken{}
  203. if err := json.Unmarshal(body, ret); err != nil {
  204. return nil, err
  205. }
  206. authrTokenKey := "authorizer_access_token_" + appid
  207. if err := cache.SetContext(stdCtx, ctx.Cache, authrTokenKey, ret.AccessToken, time.Second*time.Duration(ret.ExpiresIn-30)); err != nil {
  208. return nil, err
  209. }
  210. refreshTokenKey := "authorizer_refresh_token_" + appid
  211. if err := cache.SetContext(stdCtx, ctx.Cache, refreshTokenKey, ret.RefreshToken, 10*365*24*60*60*time.Second); err != nil {
  212. return nil, err
  213. }
  214. return ret, nil
  215. }
  216. // RefreshAuthrToken 获取(刷新)授权公众号或小程序的接口调用凭据(令牌)
  217. func (ctx *Context) RefreshAuthrToken(appid, refreshToken string) (*AuthrAccessToken, error) {
  218. return ctx.RefreshAuthrTokenContext(context.Background(), appid, refreshToken)
  219. }
  220. // GetAuthrAccessTokenContext 获取授权方AccessToken
  221. func (ctx *Context) GetAuthrAccessTokenContext(stdCtx context.Context, appid string) (string, error) {
  222. authrTokenKey := "authorizer_access_token_" + appid
  223. val := cache.GetContext(stdCtx, ctx.Cache, authrTokenKey)
  224. if val == nil {
  225. refreshTokenKey := "authorizer_refresh_token_" + appid
  226. val := cache.GetContext(stdCtx, ctx.Cache, refreshTokenKey)
  227. if val == nil {
  228. return "", fmt.Errorf("cannot get authorizer %s refresh token", appid)
  229. }
  230. token, err := ctx.RefreshAuthrTokenContext(stdCtx, appid, val.(string))
  231. if err != nil {
  232. return "", err
  233. }
  234. return token.AccessToken, nil
  235. }
  236. return val.(string), nil
  237. }
  238. // GetAuthrAccessToken 获取授权方AccessToken
  239. func (ctx *Context) GetAuthrAccessToken(appid string) (string, error) {
  240. return ctx.GetAuthrAccessTokenContext(context.Background(), appid)
  241. }
  242. // AuthorizerInfo 授权方详细信息
  243. type AuthorizerInfo struct {
  244. NickName string `json:"nick_name"`
  245. HeadImg string `json:"head_img"`
  246. ServiceTypeInfo ID `json:"service_type_info"`
  247. VerifyTypeInfo ID `json:"verify_type_info"`
  248. UserName string `json:"user_name"`
  249. PrincipalName string `json:"principal_name"`
  250. BusinessInfo struct {
  251. OpenStore string `json:"open_store"`
  252. OpenScan string `json:"open_scan"`
  253. OpenPay string `json:"open_pay"`
  254. OpenCard string `json:"open_card"`
  255. OpenShake string `json:"open_shake"`
  256. }
  257. Alias string `json:"alias"`
  258. QrcodeURL string `json:"qrcode_url"`
  259. MiniProgramInfo *MiniProgramInfo `json:"MiniProgramInfo"`
  260. RegisterType int `json:"register_type"`
  261. AccountStatus int `json:"account_status"`
  262. BasicConfig *AuthorizerBasicConfig `json:"basic_config"`
  263. }
  264. // AuthorizerBasicConfig 授权账号的基础配置结构体
  265. type AuthorizerBasicConfig struct {
  266. IsPhoneConfigured bool `json:"isPhoneConfigured"`
  267. IsEmailConfigured bool `json:"isEmailConfigured"`
  268. }
  269. // MiniProgramInfo 授权账号小程序配置 授权账号为小程序时存在
  270. type MiniProgramInfo struct {
  271. Network struct {
  272. RequestDomain []string `json:"RequestDomain"`
  273. WsRequestDomain []string `json:"WsRequestDomain"`
  274. UploadDomain []string `json:"UploadDomain"`
  275. DownloadDomain []string `json:"DownloadDomain"`
  276. BizDomain []string `json:"BizDomain"`
  277. UDPDomain []string `json:"UDPDomain"`
  278. } `json:"network"`
  279. Categories []CategoriesInfo `json:"categories"`
  280. }
  281. // CategoriesInfo 授权账号小程序配置的类目信息
  282. type CategoriesInfo struct {
  283. First string `wx:"first"`
  284. Second string `wx:"second"`
  285. }
  286. // GetAuthrInfoContext 获取授权方的帐号基本信息
  287. func (ctx *Context) GetAuthrInfoContext(stdCtx context.Context, appid string) (*AuthorizerInfo, *AuthBaseInfo, error) {
  288. cat, err := ctx.GetComponentAccessTokenContext(stdCtx)
  289. if err != nil {
  290. return nil, nil, err
  291. }
  292. req := map[string]string{
  293. "component_appid": ctx.AppID,
  294. "authorizer_appid": appid,
  295. }
  296. uri := fmt.Sprintf(getComponentInfoURL, cat)
  297. body, err := util.PostJSONContext(stdCtx, uri, req)
  298. if err != nil {
  299. return nil, nil, err
  300. }
  301. var ret struct {
  302. AuthorizerInfo *AuthorizerInfo `json:"authorizer_info"`
  303. AuthorizationInfo *AuthBaseInfo `json:"authorization_info"`
  304. }
  305. if err := json.Unmarshal(body, &ret); err != nil {
  306. return nil, nil, err
  307. }
  308. return ret.AuthorizerInfo, ret.AuthorizationInfo, nil
  309. }
  310. // GetAuthrInfo 获取授权方的帐号基本信息
  311. func (ctx *Context) GetAuthrInfo(appid string) (*AuthorizerInfo, *AuthBaseInfo, error) {
  312. return ctx.GetAuthrInfoContext(context.Background(), appid)
  313. }