Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update stable access #8

Merged
merged 5 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ module github.com/go-pay/wechat-sdk
go 1.21

require (
github.com/go-pay/bm v0.0.4
github.com/go-pay/bm v0.0.5
github.com/go-pay/crypto v0.0.1
github.com/go-pay/util v0.0.3
github.com/go-pay/xhttp v0.0.2
github.com/go-pay/smap v0.0.2
github.com/go-pay/util v0.0.4
github.com/go-pay/xhttp v0.0.3
github.com/go-pay/xlog v0.0.3
github.com/go-pay/xtime v0.0.2
)
14 changes: 8 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
github.com/go-pay/bm v0.0.4 h1:MUECRx1t0MkbQ7Yzk2qseKBOKoUi4N8WZ+alZBrZesg=
github.com/go-pay/bm v0.0.4/go.mod h1:S7ZAxWtyjm7PX54cna4N/RzJ1JAZG8EDukZ2fZaZ+qk=
github.com/go-pay/bm v0.0.5 h1:ZAg6j1Wagc8JZ88ja7VgEF/g+kKOFzLRNc43bm0ivZc=
github.com/go-pay/bm v0.0.5/go.mod h1:S7ZAxWtyjm7PX54cna4N/RzJ1JAZG8EDukZ2fZaZ+qk=
github.com/go-pay/crypto v0.0.1 h1:B6InT8CLfSLc6nGRVx9VMJRBBazFMjr293+jl0lLXUY=
github.com/go-pay/crypto v0.0.1/go.mod h1:41oEIvHMKbNcYlWUlRWtsnC6+ASgh7u29z0gJXe5bes=
github.com/go-pay/util v0.0.3 h1:0OjERb7MAVpM2gLPnBESLdMsosYyJ4i31V2/YZBiPjw=
github.com/go-pay/util v0.0.3/go.mod h1:qM8VbyF1n7YAPZBSJONSPMPsPedhUTktewUAdf1AjPg=
github.com/go-pay/xhttp v0.0.2 h1:O8rnd/d03WsboFtUthwFMg61ikHRfYHyD1m0JiUx60g=
github.com/go-pay/xhttp v0.0.2/go.mod h1:BnuvXpLKkXTFMOBc5MTb0hxdrstwunbzQPJUZOsNbt4=
github.com/go-pay/smap v0.0.2 h1:kKflYor5T5FgZltPFBMTFfjJvqYMHr5VnIFSEyhVTcA=
github.com/go-pay/smap v0.0.2/go.mod h1:HW9oAo0okuyDYsbpbj5fJFxnNj/BZorRGFw26SxrNWw=
github.com/go-pay/util v0.0.4 h1:TuwSU9o3Qd7m9v1PbzFuIA/8uO9FJnA6P7neG/NwPyk=
github.com/go-pay/util v0.0.4/go.mod h1:Tsdhs8Ib9J9b4+NKNO1PHh5hWHhlg98PthsX0ckq6PM=
github.com/go-pay/xhttp v0.0.3 h1:9Vke2QeY0xs8E9oyb3bi94v47N25ZdGgZOIG1hgCgKA=
github.com/go-pay/xhttp v0.0.3/go.mod h1:LDNKLp+C6UJRZSAcxI4z4BYtRs3ksbgxPQl1W9HQGXs=
github.com/go-pay/xlog v0.0.3 h1:avyMhCL/JgBHreoGx/am/kHxfs1udDOAeVqbmzP/Yes=
github.com/go-pay/xlog v0.0.3/go.mod h1:mH47xbobrdsSHWsmFtSF5agWbMHFP+tK0ZbVCk5OAEw=
github.com/go-pay/xtime v0.0.2 h1:7YR4/iuELsEHpJ6LUO0SVK80hQxDO9MLCfuVYIiTCRM=
Expand Down
76 changes: 19 additions & 57 deletions mini/access_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,10 @@ import (
"context"
"fmt"
"runtime"
"strconv"
"time"
)

// 获取小程序全局唯一后台接口调用凭据(access_token)
// 微信小程序文档:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
//func (s *SDK) getAccessToken() (err error) {
// defer func() {
// if err != nil {
// // reset default refresh internal
// s.RefreshInternal = time.Second * 20
// if s.callback != nil {
// go s.callback("", "", 0, err)
// }
// }
// }()
//
// path := "/cgi-bin/token?grant_type=client_credential&appid=" + s.Appid + "&secret=" + s.Secret
// at := &AccessToken{}
// if _, err = s.DoRequestGet(s.ctx, path, at); err != nil {
// return
// }
// if at.Errcode != Success {
// err = fmt.Errorf("errcode(%d), errmsg(%s)", at.Errcode, at.Errmsg)
// return
// }
// s.accessToken = at.AccessToken
// s.RefreshInternal = time.Second * time.Duration(at.ExpiresIn)
// if s.callback != nil {
// go s.callback(s.Appid, at.AccessToken, at.ExpiresIn, nil)
// }
// return nil
//}

//func (s *SDK) goAutoRefreshAccessToken() {
// defer func() {
// if r := recover(); r != nil {
// buf := make([]byte, 64<<10)
// buf = buf[:runtime.Stack(buf, false)]
// s.logger.Errorf("mini_goAutoRefreshAccessToken: panic recovered: %s\n%s", r, buf)
// }
// }()
// for {
// // every one hour, request new access token, default 10s
// time.Sleep(s.RefreshInternal / 2)
// err := s.getAccessToken()
// if err != nil {
// s.logger.Errorf("get access token error, after 10s retry: %+v", err)
// continue
// }
// }
//}
"github.com/go-pay/bm"
)

// 获取稳定版接口调用凭据
// 微信小程序文档:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getStableAccessToken.html
Expand All @@ -70,9 +22,14 @@ func (s *SDK) getStableAccessToken() (err error) {
}
}()

path := "/cgi-bin/stable_token?grant_type=client_credential&appid=" + s.Appid + "&secret=" + s.Secret + "&force_refresh=false"
path := "/cgi-bin/stable_token"
body := make(bm.BodyMap)
body.Set("grant_type", "client_credential").
Set("appid", s.Appid).
Set("secret", s.Secret).
Set("force_refresh", false)
at := &AccessToken{}
if _, err = s.DoRequestGet(s.ctx, path, at); err != nil {
if _, err = s.doRequestPost(s.ctx, path, body, at); err != nil {
return
}
if at.Errcode != Success {
Expand Down Expand Up @@ -106,7 +63,7 @@ func (s *SDK) goAutoRefreshStableAccessToken() {
time.Sleep(s.RefreshInternal / 2)
err := s.getStableAccessToken()
if err != nil {
s.logger.Errorf("get access token error, after 10s retry: %+v", err)
s.logger.Errorf("get stable access token error, after 10s retry: %+v", err)
continue
}
}
Expand All @@ -129,7 +86,7 @@ func (s *SDK) SetMiniAccessToken(accessToken string) {

// =====================================================================================================================

// 获取接口调用凭据
// 获取 Access Token
// 微信小程序文档:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
func GetAccessToken(c context.Context, appid, secret string) (at *AccessToken, err error) {
uri := HostDefault + "/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret
Expand All @@ -143,12 +100,17 @@ func GetAccessToken(c context.Context, appid, secret string) (at *AccessToken, e
return at, nil
}

// 获取稳定版接口调用凭据
// 获取 Stable Access Token
// 微信小程序文档:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getStableAccessToken.html
func GetStableAccessToken(c context.Context, appid, secret string, forceRefresh bool) (at *AccessToken, err error) {
uri := HostDefault + "/cgi-bin/stable_token?grant_type=client_credential&appid=" + appid + "&secret=" + secret + "&force_refresh=" + strconv.FormatBool(forceRefresh)
url := HostDefault + "/cgi-bin/stable_token"
body := make(bm.BodyMap)
body.Set("grant_type", "client_credential").
Set("appid", appid).
Set("secret", secret).
Set("force_refresh", forceRefresh)
at = &AccessToken{}
if err = doRequestGet(c, uri, at); err != nil {
if err = doRequestPost(c, url, body, at); err != nil {
return nil, err
}
if at.Errcode != Success {
Expand Down
14 changes: 14 additions & 0 deletions mini/mini.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
"time"

"github.com/go-pay/bm"
"github.com/go-pay/util"
"github.com/go-pay/util/js"
"github.com/go-pay/wechat-sdk"
Expand Down Expand Up @@ -97,3 +98,16 @@ func doRequestGet(c context.Context, uri string, ptr any) (err error) {
}
return
}

func doRequestPost(c context.Context, url string, body bm.BodyMap, ptr any) (err error) {
req := xhttp.NewClient().Req()
req.Header.Add(wechat.HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
_, bs, err := req.Post(url).SendBodyMap(body).EndBytes(c)
if err != nil {
return fmt.Errorf("http.request(POST, %s), err:%w", url, err)
}
if err = js.UnmarshalBytes(bs, ptr); err != nil {
return fmt.Errorf("js.UnmarshalBytes(%s, %+v):%w", string(bs), ptr, err)
}
return
}
52 changes: 20 additions & 32 deletions open/access_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ func (s *SDK) refreshAccessToken(openid, refreshToken string) {
}
return
}
s.mu.Lock()
s.openidAccessTokenMap[at.Openid] = at
s.mu.Unlock()

s.openidAccessTokenMap.Store(at.Openid, at)
if s.callback != nil {
go s.callback(&AT{
AccessToken: at.AccessToken,
Expand All @@ -53,12 +52,13 @@ func (s *SDK) goAutoRefreshAccessTokenJob() {
for {
// request new access token, default internal 10min
time.Sleep(s.autoRefreshTokenInternal)
for k, v := range s.openidAccessTokenMap {
s.openidAccessTokenMap.Range(func(k string, v *AccessToken) bool {
// 有效期小于1.5倍轮询时间时,自动刷新)
if time.Duration(v.ExpiresIn)*time.Second < (s.autoRefreshTokenInternal*3)/2 {
s.refreshAccessToken(k, v.RefreshToken)
}
}
return true
})
}
}

Expand All @@ -74,30 +74,24 @@ func (s *SDK) SetAccessTokenRefreshInternal(internal time.Duration) {

// GetAccessTokenMap 获取 access_token map,key 为 openid
func (s *SDK) GetAccessTokenMap() (openidATMap map[string]*AT) {
openidATMap = make(map[string]*AT, len(s.openidAccessTokenMap))
if s.openidAccessTokenMap != nil && len(s.openidAccessTokenMap) > 0 {
s.mu.RLock()
defer s.mu.RUnlock()
for k, v := range s.openidAccessTokenMap {
openidATMap[k] = &AT{
AccessToken: v.AccessToken,
ExpiresIn: v.ExpiresIn,
RefreshToken: v.RefreshToken,
Openid: v.Openid,
Scope: v.Scope,
Unionid: v.Unionid,
}
openidATMap = make(map[string]*AT)
s.openidAccessTokenMap.Range(func(k string, v *AccessToken) bool {
openidATMap[k] = &AT{
AccessToken: v.AccessToken,
ExpiresIn: v.ExpiresIn,
RefreshToken: v.RefreshToken,
Openid: v.Openid,
Scope: v.Scope,
Unionid: v.Unionid,
}
return
}
return true
})
return
}

// DelAccessToken 根据 openid 删除 map 中维护的 access_token
func (s *SDK) DelAccessToken(openid string) {
if s.openidAccessTokenMap != nil {
delete(s.openidAccessTokenMap, openid)
}
s.openidAccessTokenMap.Delete(openid)
}

// Code2AccessToken 通过 code 获取用户 access_token
Expand All @@ -123,9 +117,7 @@ func (s *SDK) Code2AccessToken(c context.Context, code string) (at *AccessToken,
}, nil)
}
if s.autoManageToken {
s.mu.Lock()
s.openidAccessTokenMap[at.Openid] = at
s.mu.Unlock()
s.openidAccessTokenMap.Store(at.Openid, at)
}
return at, nil
}
Expand Down Expand Up @@ -153,9 +145,7 @@ func (s *SDK) RefreshAccessToken(c context.Context, refreshToken string) (at *Ac
}, nil)
}
if s.autoManageToken {
s.mu.Lock()
s.openidAccessTokenMap[at.Openid] = at
s.mu.Unlock()
s.openidAccessTokenMap.Store(at.Openid, at)
}
return at, nil
}
Expand All @@ -171,9 +161,7 @@ func (s *SDK) CheckAccessToken(c context.Context, accessToken, openid string) (e
}
if ec.Errcode != Success {
if s.autoManageToken {
s.mu.Lock()
delete(s.openidAccessTokenMap, openid)
s.mu.Unlock()
s.DelAccessToken(openid)
}
err = fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg)
return err
Expand Down
10 changes: 4 additions & 6 deletions open/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"context"
"fmt"
"net/http"
"sync"
"time"

"github.com/go-pay/smap"
"github.com/go-pay/util"
"github.com/go-pay/util/js"
"github.com/go-pay/wechat-sdk"
Expand All @@ -17,13 +17,12 @@ import (
type SDK struct {
ctx context.Context
DebugSwitch wechat.DebugSwitch
mu sync.RWMutex
Appid string
Secret string
Host string
autoManageToken bool // 是否自动维护刷新 AccessToken
autoRefreshTokenInternal time.Duration // 自动刷新 token 的间隔时间
openidAccessTokenMap map[string]*AccessToken // key: openid
autoManageToken bool // 是否自动维护刷新 AccessToken
autoRefreshTokenInternal time.Duration // 自动刷新 token 的间隔时间
openidAccessTokenMap smap.Map[string, *AccessToken] // key: openid
hc *xhttp.Client
logger xlog.XLogger

Expand All @@ -49,7 +48,6 @@ func New(appid, secret string, autoManageToken bool) (o *SDK) {
}
if autoManageToken {
o.autoRefreshTokenInternal = time.Minute * 10
o.openidAccessTokenMap = make(map[string]*AccessToken)
go o.goAutoRefreshAccessTokenJob()
}
return
Expand Down
Loading