From 5c4a10cc56deda2e42360303e9b5d0454f780092 Mon Sep 17 00:00:00 2001 From: Jerry <85411418@qq.com> Date: Wed, 8 Jun 2022 22:24:53 +0800 Subject: [PATCH] v1.1.0 --- README.md | 40 +++++++++++++++--- access_token.go | 10 ++++- common.go | 2 +- doc/mini.md | 2 +- doc/open.md | 34 ++++++++++++--- doc/public.md | 2 +- mini/customer_service.go | 28 +++++++++---- mini/login.go | 6 ++- mini/model.go | 2 + mini/uniform_message.go | 12 ++++-- mini/user_info.go | 16 ++++++-- open/access_token.go | 89 ++++++++++++++++++++++++---------------- open/model.go | 19 +++++++++ open/open.go | 8 ++-- open/user_info.go | 26 ++++++++++++ public/account_manage.go | 16 ++++++-- public/model.go | 2 + public/user_manage.go | 69 +++++++++++++++++++++---------- release_note.txt | 5 +++ sdk.go | 19 ++++----- sdk_mini_test.go | 3 +- sdk_open_test.go | 16 ++++++++ sdk_public_test.go | 3 +- sdk_test.go | 29 +++++++++---- 24 files changed, 340 insertions(+), 118 deletions(-) create mode 100644 open/user_info.go create mode 100644 sdk_open_test.go diff --git a/README.md b/README.md index 6b3bfb7..de1da62 100644 --- a/README.md +++ b/README.md @@ -64,26 +64,30 @@ if err != nil { //wxsdk.DebugSwitch = wechat.DebugOn ``` -- ### AccessToken 说明 +- ### AccessToken 说明:微信小程序和公众号 与 开放平台的 AccessToken 不用通用。 +- #### 微信小程序 or 公众号的 AccessToken ```go -// NewSDK 后,首次获取AccessToken请通过此方法获取,之后请通过下面的回调方法获取 -at := wxsdk.GetAccessToken() +// New完SDK,首次获取AccessToken请通过此方法获取,之后请通过下面的回调方法获取 +at := wxsdk.GetMiniOrPublicAT() xlog.Infof("at: %s", at) // 每次刷新 accessToken 后,此方法回调返回 accessToken 和 有效时间(秒) -wxsdk.SetAccessTokenCallback(func(accessToken string, expireIn int, err error) { +wxsdk.SetMiniOrPublicATCallback(func(accessToken string, expireIn int, err error) { if err != nil { xlog.Errorf("refresh access token error(%+v)", err) + return } xlog.Infof("accessToken: %s", accessToken) xlog.Infof("expireIn: %d", expireIn) }) // 若 NewSDK() 时自传 AccessToken,则后续更新替换请调用此方法 -wxsdk.SetAccessToken() +wxsdk.SetMiniOrPublicAT() ``` +- #### 开放平台 的AccessToken详见下方 NewOpenSDK + - ### NewMiniSDK ```go @@ -98,6 +102,32 @@ miniSDK := wxsdk.NewMini() publicSDK := wxsdk.NewPublic() ``` +- ### NewOpenSDK + +```go +// New 微信开放平台 SDK +openSDK, err = wxsdk.NewOpen() +if err != nil { + xlog.Error(err) + return +} +// 注意:必须换取 开放平台 自己的AccessToken,与小程序和公众号不通用 +openAT, err := openSDK.Code2AccessToken(ctx, "xxx") +if err != nil { + xlog.Error(err) + return +} +xlog.Infof("open at: %+v", openAT) +// 每次刷新 accessToken 后,此方法回调返回 accessToken 和 有效时间(秒) +openSDK.SetOpenATCallback(func(at *open.AccessToken, err error) { + if err != nil { + xlog.Errorf("refresh access token error(%+v)", err) + return + } + xlog.Infof("AccessToken: %+v", at) +}) +``` + - ### 点击分别查看小程序、公众号使用文档 * #### [微信小程序](https://github.com/go-pay/wechat-sdk/blob/main/doc/mini.md) diff --git a/access_token.go b/access_token.go index af3d7ba..40d54b9 100644 --- a/access_token.go +++ b/access_token.go @@ -2,6 +2,7 @@ package wechat import ( "fmt" + "runtime" "time" "github.com/go-pay/wechat-sdk/mini" @@ -44,7 +45,14 @@ func (s *SDK) getAccessToken() (err error) { return nil } -func (s *SDK) autoRefreshAccessToken() { +func (s *SDK) goAutoRefreshAccessToken() { + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 64<<10) + buf = buf[:runtime.Stack(buf, false)] + xlog.Errorf("mini_public_goAutoRefreshAccessToken: panic recovered: %s\n%s", r, buf) + } + }() for { // every one hour, request new access token, default 10s time.Sleep(s.RefreshInternal / 2) diff --git a/common.go b/common.go index fcdefea..bf8a6de 100644 --- a/common.go +++ b/common.go @@ -14,7 +14,7 @@ const ( DebugOff = 0 DebugOn = 1 - Version = "1.0.0" + Version = "1.1.0" ) const ( diff --git a/doc/mini.md b/doc/mini.md index 97d8fd5..d30a516 100644 --- a/doc/mini.md +++ b/doc/mini.md @@ -8,7 +8,7 @@ --- -### 具体使用请参考 `doc/mini.md` +### 具体使用请参考 `sdk_mini_test.go` #### Code2Session diff --git a/doc/open.md b/doc/open.md index 4a0b10e..4b4a02c 100644 --- a/doc/open.md +++ b/doc/open.md @@ -6,12 +6,37 @@ --- -### 具体使用请参考 `doc/open.md` +### 具体使用请参考 `sdk_open_test.go` #### 通过 code 获取 access_token ```go +// 注意:必须换取 开放平台 自己的AccessToken,与小程序和公众号不通用 +openAT, err := openSDK.Code2AccessToken(ctx, "xxx") +if err != nil { + xlog.Error(err) + return +} +xlog.Infof("open at: %+v", openAT) +// 每次刷新 accessToken 后,此方法回调返回 accessToken 和 有效时间(秒) +openSDK.SetOpenATCallback(func(at *open.AccessToken, err error) { + if err != nil { + xlog.Errorf("refresh access token error(%+v)", err) + return + } + xlog.Infof("AccessToken: %+v", at) +}) +``` + +#### 通过 code 获取 access_token +```go +rsp, err := openSDK.UserInfo(ctx, "openid", "zh_CN") +if err != nil { + xlog.Error(err) + return +} +xlog.Infof("rsp:%+v", rsp) ``` ## 附录: @@ -19,7 +44,6 @@ ### 微信开发平台 服务端API * 微信登录功能 - * 通过 code 获取 access_token:``sdk.()` - * 刷新或续期 access_token 使用:`sdk.()` - * 检验授权凭证 access_token 是否有效:`sdk.()` - * 获取用户个人信息(UnionID 机制):`sdk.UserTagDelete()` \ No newline at end of file + * 通过 code 获取 access_token:``sdk.Code2AccessToken()` + * 检验授权凭证 access_token 是否有效:`sdk.CheckAccessToken()` + * 获取用户个人信息(UnionID 机制):`sdk.UserInfo()` \ No newline at end of file diff --git a/doc/public.md b/doc/public.md index 1ca6ee2..a2d55ea 100644 --- a/doc/public.md +++ b/doc/public.md @@ -6,7 +6,7 @@ --- -### 具体使用请参考 `doc/open.md` +### 具体使用请参考 `sdk_public_test.go` #### 创建二维码 diff --git a/mini/customer_service.go b/mini/customer_service.go index 784c56e..c3379f1 100644 --- a/mini/customer_service.go +++ b/mini/customer_service.go @@ -2,6 +2,7 @@ package mini import ( "context" + "fmt" "github.com/go-pay/wechat-sdk/pkg/bmap" "github.com/go-pay/wechat-sdk/pkg/util" @@ -25,7 +26,7 @@ func (s *SDK) CSMessageGetTempMedia(c context.Context, mediaId string) (media [] // msgType:消息类型,枚举值:mini.MsgTypeText、mini.MsgTypeImage、mini.MsgTypeLink、mini.MsgTypeMiniPage // msgValue:对应 msgType 的value值,BodyMap key-value 格式传入 // 文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/customer-message/customerServiceMessage.send.html -func (s *SDK) CSMessageSend(c context.Context, toUser string, msgType MsgType, msgValue bmap.BodyMap) (ec *ErrorCode, err error) { +func (s *SDK) CSMessageSend(c context.Context, toUser string, msgType MsgType, msgValue bmap.BodyMap) (err error) { path := "/cgi-bin/message/custom/send?access_token=" + s.Conf.AccessToken body := make(bmap.BodyMap) body.Set("touser", toUser) @@ -43,11 +44,14 @@ func (s *SDK) CSMessageSend(c context.Context, toUser string, msgType MsgType, m body.Set("msgtype", "miniprogrampage"). Set("text", msgValue) } - ec = &ErrorCode{} + ec := &ErrorCode{} if err = s.doRequestPost(c, path, body, ec); err != nil { - return nil, err + return err } - return + if ec.Errcode != Success { + return fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg) + } + return nil } // CSMessageSetTyping 下发客服当前输入状态给用户 @@ -55,7 +59,7 @@ func (s *SDK) CSMessageSend(c context.Context, toUser string, msgType MsgType, m // toUser:小程序用户的 OpenID // typingStatus:枚举值:mini.TypingTyping、mini.TypingCancel // 文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/customer-message/customerServiceMessage.setTyping.html -func (s *SDK) CSMessageSetTyping(c context.Context, toUser string, typingStatus TypingStatus) (ec *ErrorCode, err error) { +func (s *SDK) CSMessageSetTyping(c context.Context, toUser string, typingStatus TypingStatus) (err error) { path := "/cgi-bin/message/custom/typing?access_token=" + s.Conf.AccessToken body := make(bmap.BodyMap) body.Set("touser", toUser) @@ -65,11 +69,14 @@ func (s *SDK) CSMessageSetTyping(c context.Context, toUser string, typingStatus case TypingCancel: body.Set("command", "CancelTyping") } - ec = &ErrorCode{} + ec := &ErrorCode{} if err = s.doRequestPost(c, path, body, ec); err != nil { - return nil, err + return err } - return + if ec.Errcode != Success { + return fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg) + } + return nil } // CSMessageUploadTempMedia 把媒体文件上传到微信服务器 @@ -85,5 +92,8 @@ func (s *SDK) CSMessageUploadTempMedia(c context.Context, img *util.File) (media if err = s.doRequestPostFile(c, path, body, media); err != nil { return nil, err } - return + if media.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", media.Errcode, media.Errmsg) + } + return media, nil } diff --git a/mini/login.go b/mini/login.go index 3743a78..fd6ee0c 100644 --- a/mini/login.go +++ b/mini/login.go @@ -2,6 +2,7 @@ package mini import ( "context" + "fmt" ) // Code2Session 登录凭证校验 @@ -14,5 +15,8 @@ func (s *SDK) Code2Session(c context.Context, wxCode string) (session *Code2Sess if err = s.doRequestGet(c, path, session); err != nil { return nil, err } - return + if session.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", session.Errcode, session.Errmsg) + } + return session, nil } diff --git a/mini/model.go b/mini/model.go index a0f23d1..2212f76 100644 --- a/mini/model.go +++ b/mini/model.go @@ -3,6 +3,8 @@ package mini import "github.com/go-pay/wechat-sdk/pkg/xtime" const ( + Success = 0 + DebugOff = 0 DebugOn = 1 diff --git a/mini/uniform_message.go b/mini/uniform_message.go index 3f3c9ab..6dd6a02 100644 --- a/mini/uniform_message.go +++ b/mini/uniform_message.go @@ -2,6 +2,7 @@ package mini import ( "context" + "fmt" "github.com/go-pay/wechat-sdk/pkg/bmap" ) @@ -11,14 +12,17 @@ import ( // toUser:用户openid,可以是小程序的openid,也可以是mp_template_msg.appid对应的公众号的openid // mpMsg:对应 mp_template_msg 的value值,BodyMap key-value 格式传入 // 文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html -func (s *SDK) UniformMessageSend(c context.Context, toUser string, mpMsg bmap.BodyMap) (ec *ErrorCode, err error) { +func (s *SDK) UniformMessageSend(c context.Context, toUser string, mpMsg bmap.BodyMap) (err error) { path := "/cgi-bin/message/wxopen/template/uniform_send?access_token=" + s.Conf.AccessToken body := make(bmap.BodyMap) body.Set("touser", toUser) body.Set("mp_template_msg", mpMsg) - ec = &ErrorCode{} + ec := &ErrorCode{} if err = s.doRequestPost(c, path, body, ec); err != nil { - return nil, err + return err } - return + if ec.Errcode != Success { + return fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg) + } + return nil } diff --git a/mini/user_info.go b/mini/user_info.go index aa1c5f9..7612e8c 100644 --- a/mini/user_info.go +++ b/mini/user_info.go @@ -4,6 +4,7 @@ import ( "context" "crypto/sha256" "encoding/hex" + "fmt" "github.com/go-pay/wechat-sdk/pkg/bmap" ) @@ -19,7 +20,10 @@ func (s *SDK) GetPaidUnionid(c context.Context, openid, transactionId string) (u if err = s.doRequestGet(c, path, unionid); err != nil { return nil, err } - return + if unionid.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", unionid.Errcode, unionid.Errmsg) + } + return unionid, nil } // GetPaidUnionidByTradeNo 用户支付完成后,获取该用户的 UnionId,无需用户授权 @@ -34,7 +38,10 @@ func (s *SDK) GetPaidUnionidByTradeNo(c context.Context, openid, mchid, tradeNo if err = s.doRequestGet(c, path, unionid); err != nil { return nil, err } - return + if unionid.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", unionid.Errcode, unionid.Errmsg) + } + return unionid, nil } // CheckEncryptedData 检查加密信息是否由微信生成 @@ -52,5 +59,8 @@ func (s *SDK) CheckEncryptedData(c context.Context, encryptedData string) (resul if err = s.doRequestPost(c, path, body, result); err != nil { return nil, err } - return + if result.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", result.Errcode, result.Errmsg) + } + return result, nil } diff --git a/open/access_token.go b/open/access_token.go index ced7fc9..6d2b878 100644 --- a/open/access_token.go +++ b/open/access_token.go @@ -1,37 +1,14 @@ package open import ( + "context" "fmt" + "runtime" "time" "github.com/go-pay/wechat-sdk/pkg/xlog" ) -// Code2AccessToken 获取开放平台全局唯一后台接口调用凭据(access_token) -// 微信开放平台文档:https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html -func (s *SDK) Code2AccessToken(code string) (err error) { - path := "/sns/oauth2/access_token?grant_type=authorization_code&appid=" + s.Appid + "&secret=" + s.Secret + "&code=" + code - 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(at.AccessToken, at.ExpiresIn, nil) - } - if len(s.atChanMap) > 0 { - for _, v := range s.atChanMap { - v <- at.AccessToken - } - } - return nil -} - // 刷新或续期 access_token 使用 // 微信开放平台文档:https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html func (s *SDK) refreshAccessToken() (err error) { @@ -40,7 +17,7 @@ func (s *SDK) refreshAccessToken() (err error) { // reset default refresh internal s.RefreshInternal = time.Second * 20 if s.callback != nil { - go s.callback("", 0, err) + go s.callback(nil, err) } } }() @@ -57,17 +34,19 @@ func (s *SDK) refreshAccessToken() (err error) { s.accessToken = at.AccessToken s.RefreshInternal = time.Second * time.Duration(at.ExpiresIn) if s.callback != nil { - go s.callback(at.AccessToken, at.ExpiresIn, nil) - } - if len(s.atChanMap) > 0 { - for _, v := range s.atChanMap { - v <- at.AccessToken - } + go s.callback(at, nil) } return nil } -func (s *SDK) autoRefreshAccessToken() { +func (s *SDK) goAutoRefreshAccessToken() { + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 64<<10) + buf = buf[:runtime.Stack(buf, false)] + xlog.Errorf("open_goAutoRefreshAccessToken: panic recovered: %s\n%s", r, buf) + } + }() for { // every one hour, request new access token, default 10s time.Sleep(s.RefreshInternal / 2) @@ -79,12 +58,50 @@ func (s *SDK) autoRefreshAccessToken() { } } -// SetAccessTokenCallback access token callback listener -func (s *SDK) SetAccessTokenCallback(fn func(accessToken string, expireIn int, err error)) { +// SetOpenATCallback open access token callback listener +func (s *SDK) SetOpenATCallback(fn func(at *AccessToken, err error)) { s.callback = fn } -// GetAccessToken get access token string +// Code2AccessToken 获取开放平台全局唯一后台接口调用凭据(access_token) +// 注意:必须换取 开放平台 自己的AccessToken,与小程序和公众号不通用 +// 微信开放平台文档:https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html +func (s *SDK) Code2AccessToken(c context.Context, code string) (at *AccessToken, err error) { + path := "/sns/oauth2/access_token?grant_type=authorization_code&appid=" + s.Appid + "&secret=" + s.Secret + "&code=" + code + at = &AccessToken{} + if err = s.DoRequestGet(c, 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(at, nil) + } + // 自动刷新 AccessToken + go s.goAutoRefreshAccessToken() + return at, nil +} + +// CheckAccessToken check access_token is ok +// 文档:https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Authorized_API_call_UnionID.html +func (s *SDK) CheckAccessToken(c context.Context, openid string) (err error) { + path := "/sns/auth?access_token=" + s.accessToken + "&openid=" + openid + ec := &ErrorCode{} + if err = s.DoRequestGet(c, path, ec); err != nil { + return + } + if ec.Errcode != Success { + err = fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg) + return + } + return nil +} + +// GetAccessToken get open access_token string func (s *SDK) GetAccessToken() (at string) { return s.accessToken } diff --git a/open/model.go b/open/model.go index c6b6421..e78949d 100644 --- a/open/model.go +++ b/open/model.go @@ -27,3 +27,22 @@ type AccessToken struct { Errcode int `json:"errcode,omitempty"` // 错误码 Errmsg string `json:"errmsg,omitempty"` // 错误信息 } + +type ErrorCode struct { + Errcode int `json:"errcode,omitempty"` // 错误码 + Errmsg string `json:"errmsg,omitempty"` // 错误信息 +} + +type UserInfo struct { + Openid string `json:"openid,omitempty"` // 普通用户的标识,对当前开发者帐号唯一 + Nickname string `json:"nickname,omitempty"` // 普通用户昵称 + Sex int `json:"sex,omitempty"` // 普通用户性别,1 为男性,2 为女性 + Province string `json:"province,omitempty"` // 普通用户个人资料填写的省份 + City string `json:"city,omitempty"` // 普通用户个人资料填写的城市 + Country string `json:"country,omitempty"` // 国家,如中国为 CN + Headimgurl string `json:"headimgurl,omitempty"` // 用户头像,最后一个数值代表正方形头像大小(有 0、46、64、96、132 数值可选,0 代表 640*640 正方形头像),用户没有头像时该项为空 + Privilege []string `json:"privilege,omitempty"` // 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom) + Unionid string `json:"unionid,omitempty"` // 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的 unionid 是唯一的。 + Errcode int `json:"errcode,omitempty"` // 错误码 + Errmsg string `json:"errmsg,omitempty"` // 错误信息 +} diff --git a/open/open.go b/open/open.go index ff6f915..649b568 100644 --- a/open/open.go +++ b/open/open.go @@ -20,11 +20,9 @@ type SDK struct { Host string Appid string Secret string - - accessToken string - refreshToken string - atChanMap map[string]chan string - callback func(accessToken string, expireIn int, err error) + accessToken string + refreshToken string + callback func(at *AccessToken, err error) } func New(c *Config, ds int8) (o *SDK) { diff --git a/open/user_info.go b/open/user_info.go new file mode 100644 index 0000000..abda042 --- /dev/null +++ b/open/user_info.go @@ -0,0 +1,26 @@ +package open + +import ( + "context" + "fmt" +) + +// UserInfo 获取用户个人信息(UnionID 机制) +// 注意:errcode = 0 为成功 +// 文档:https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Authorized_API_call_UnionID.html +func (s *SDK) UserInfo(c context.Context, openid, lan string) (ui *UserInfo, err error) { + switch lan { + case "zh_CN", "zh_TW", "en": + default: + lan = "en" + } + path := "/sns/userinfo?access_token=" + s.accessToken + "&openid=" + openid + "&lang=" + lan + ui = &UserInfo{} + if err = s.DoRequestGet(c, path, ui); err != nil { + return + } + if ui.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", ui.Errcode, ui.Errmsg) + } + return ui, nil +} diff --git a/public/account_manage.go b/public/account_manage.go index 5a10505..13e046e 100644 --- a/public/account_manage.go +++ b/public/account_manage.go @@ -2,6 +2,7 @@ package public import ( "context" + "fmt" "github.com/go-pay/wechat-sdk/pkg/bmap" ) @@ -16,7 +17,10 @@ func (s *SDK) QRCodeCreate(c context.Context, body bmap.BodyMap) (qr *QRCodeRsp, if err = s.doRequestPost(c, path, body, qr); err != nil { return nil, err } - return + if qr.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", qr.Errcode, qr.Errmsg) + } + return qr, nil } // ShortKeyGen 生成短key托管 @@ -28,7 +32,10 @@ func (s *SDK) ShortKeyGen(c context.Context, body bmap.BodyMap) (skg *ShortKeyGe if err = s.doRequestPost(c, path, body, skg); err != nil { return nil, err } - return + if skg.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", skg.Errcode, skg.Errmsg) + } + return skg, nil } // ShortKeyFetch 获取托管的短key @@ -43,5 +50,8 @@ func (s *SDK) ShortKeyFetch(c context.Context, shortKey string) (skf *ShortKeyFe if err = s.doRequestPost(c, path, body, skf); err != nil { return nil, err } - return + if skf.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", skf.Errcode, skf.Errmsg) + } + return skf, nil } diff --git a/public/model.go b/public/model.go index f2b15e6..6e07c7d 100644 --- a/public/model.go +++ b/public/model.go @@ -3,6 +3,8 @@ package public import "github.com/go-pay/wechat-sdk/pkg/xtime" const ( + Success = 0 + DebugOff = 0 DebugOn = 1 ) diff --git a/public/user_manage.go b/public/user_manage.go index cf7a3cc..892ca79 100644 --- a/public/user_manage.go +++ b/public/user_manage.go @@ -3,6 +3,7 @@ package public import ( "context" "errors" + "fmt" "github.com/go-pay/wechat-sdk/pkg/bmap" ) @@ -20,7 +21,10 @@ func (s *SDK) UserTagCreate(c context.Context, tagName string) (ut *UserTagRsp, if err = s.doRequestPost(c, path, body, ut); err != nil { return nil, err } - return + if ut.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", ut.Errcode, ut.Errmsg) + } + return ut, nil } // UserTagList 获取已创建的用户标签列表 @@ -32,40 +36,49 @@ func (s *SDK) UserTagList(c context.Context) (utl *UserTagListRsp, err error) { if err = s.doRequestGet(c, path, utl); err != nil { return nil, err } - return + if utl.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", utl.Errcode, utl.Errmsg) + } + return utl, nil } // UserTagUpdate 用户标签编辑更新 // 注意:errcode = 0 为成功 // 文档:https://developers.weixin.qq.com/doc/offiaccount/User_Management/User_Tag_Management.html -func (s *SDK) UserTagUpdate(c context.Context, tagId int, tagName string) (ec *ErrorCode, err error) { +func (s *SDK) UserTagUpdate(c context.Context, tagId int, tagName string) (err error) { path := "/cgi-bin/tags/update?access_token=" + s.Conf.AccessToken body := make(bmap.BodyMap) body.SetBodyMap("tag", func(b bmap.BodyMap) { b.Set("id", tagId) b.Set("name", tagName) }) - ec = &ErrorCode{} + ec := &ErrorCode{} if err = s.doRequestPost(c, path, body, ec); err != nil { - return nil, err + return err } - return + if ec.Errcode != Success { + return fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg) + } + return nil } // UserTagDelete 用户标签删除 // 注意:errcode = 0 为成功 // 文档:https://developers.weixin.qq.com/doc/offiaccount/User_Management/User_Tag_Management.html -func (s *SDK) UserTagDelete(c context.Context, tagId int) (ec *ErrorCode, err error) { +func (s *SDK) UserTagDelete(c context.Context, tagId int) (err error) { path := "/cgi-bin/tags/delete?access_token=" + s.Conf.AccessToken body := make(bmap.BodyMap) body.SetBodyMap("tag", func(b bmap.BodyMap) { b.Set("id", tagId) }) - ec = &ErrorCode{} + ec := &ErrorCode{} if err = s.doRequestPost(c, path, body, ec); err != nil { - return nil, err + return err + } + if ec.Errcode != Success { + return fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg) } - return + return nil } // UserTagFansList 获取标签下粉丝列表 @@ -83,43 +96,52 @@ func (s *SDK) UserTagFansList(c context.Context, tagId int, openid string) (utf if err = s.doRequestPost(c, path, body, utf); err != nil { return nil, err } - return + if utf.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", utf.Errcode, utf.Errmsg) + } + return utf, nil } // UserTagBatchTagging 批量为用户打标签 // 注意:errcode = 0 为成功 // 文档:https://developers.weixin.qq.com/doc/offiaccount/User_Management/User_Tag_Management.html -func (s *SDK) UserTagBatchTagging(c context.Context, tagId int, openidList []string) (ec *ErrorCode, err error) { +func (s *SDK) UserTagBatchTagging(c context.Context, tagId int, openidList []string) (err error) { if len(openidList) <= 0 { - return nil, errors.New("openid_list is empty") + return errors.New("openid_list is empty") } path := "/cgi-bin/tags/members/batchtagging?access_token=" + s.Conf.AccessToken body := make(bmap.BodyMap) body.Set("tagid", tagId) body.Set("openid_list", openidList) - ec = &ErrorCode{} + ec := &ErrorCode{} if err = s.doRequestPost(c, path, body, ec); err != nil { - return nil, err + return err + } + if ec.Errcode != Success { + return fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg) } - return + return nil } // UserTagBatchUnTagging 批量为用户取消标签 // 注意:errcode = 0 为成功 // 文档:https://developers.weixin.qq.com/doc/offiaccount/User_Management/User_Tag_Management.html -func (s *SDK) UserTagBatchUnTagging(c context.Context, tagId int, openidList []string) (ec *ErrorCode, err error) { +func (s *SDK) UserTagBatchUnTagging(c context.Context, tagId int, openidList []string) (err error) { if len(openidList) <= 0 { - return nil, errors.New("openid_list is empty") + return errors.New("openid_list is empty") } path := "/cgi-bin/tags/members/batchuntagging?access_token=" + s.Conf.AccessToken body := make(bmap.BodyMap) body.Set("tagid", tagId) body.Set("openid_list", openidList) - ec = &ErrorCode{} + ec := &ErrorCode{} if err = s.doRequestPost(c, path, body, ec); err != nil { - return nil, err + return err + } + if ec.Errcode != Success { + return fmt.Errorf("errcode(%d), errmsg(%s)", ec.Errcode, ec.Errmsg) } - return + return nil } // UserTagIdList 获取用户身上的标签列表 @@ -136,5 +158,8 @@ func (s *SDK) UserTagIdList(c context.Context, openid string) (uti *UserTagIdLis if err = s.doRequestPost(c, path, body, uti); err != nil { return nil, err } - return + if uti.Errcode != Success { + return nil, fmt.Errorf("errcode(%d), errmsg(%s)", uti.Errcode, uti.Errmsg) + } + return uti, nil } diff --git a/release_note.txt b/release_note.txt index 6ac60a0..a66cb4f 100644 --- a/release_note.txt +++ b/release_note.txt @@ -1,3 +1,8 @@ +版本号:Release 1.1.0 +修改记录: + (1) 比较大的更新,区分了 mini(小程序)、open(开放平台)、public(公众号)三个平台 + (2) 接口增加 errcode 是否成功的预判的处理,出错时直接返回 error + 版本号:Release 1.0.0 修改记录: (1) 首次 release diff --git a/sdk.go b/sdk.go index f2a36ac..9f33029 100644 --- a/sdk.go +++ b/sdk.go @@ -53,7 +53,7 @@ func NewSDK(plat Platform, appid, secret string) (sdk *SDK, err error) { return nil, err } // auto refresh access token - go sdk.autoRefreshAccessToken() + go sdk.goAutoRefreshAccessToken() case PlatformPublic: // 获取AccessToken err = sdk.getAccessToken() @@ -61,17 +61,17 @@ func NewSDK(plat Platform, appid, secret string) (sdk *SDK, err error) { return nil, err } // auto refresh access token - go sdk.autoRefreshAccessToken() + go sdk.goAutoRefreshAccessToken() case PlatformOpen: - + // 需要开放平台Client单独调用 s.Code2AccessToken() default: return nil, fmt.Errorf("unsupported platform: %s", plat) } return } -// SetAccessToken 若 NewSDK() 时自传 AccessToken,则后续更新替换请调用此方法 -func (s *SDK) SetAccessToken(accessToken string) { +// SetMiniOrPublicAT 若 NewSDK() 时自传 AccessToken,则后续更新替换请调用此方法 +func (s *SDK) SetMiniOrPublicAT(accessToken string) { s.accessToken = accessToken if len(s.atChanMap) > 0 { for _, v := range s.atChanMap { @@ -131,11 +131,10 @@ func (s *SDK) NewOpen() (o *open.SDK, err error) { return nil, fmt.Errorf("invalid platform: %s", s.plat) } c := &open.Config{ - Ctx: s.ctx, - Appid: s.Appid, - Secret: s.Secret, - AccessToken: s.accessToken, - Host: s.Host, + Ctx: s.ctx, + Appid: s.Appid, + Secret: s.Secret, + Host: s.Host, } return open.New(c, int8(s.DebugSwitch)), nil } diff --git a/sdk_mini_test.go b/sdk_mini_test.go index 8725019..9e48256 100644 --- a/sdk_mini_test.go +++ b/sdk_mini_test.go @@ -30,12 +30,11 @@ func TestUniformMessageSend(t *testing.T) { //xlog.Debugf("%s", body.JsonBody()) - rsp, err := miniSDK.UniformMessageSend(ctx, "Openid", body) + err = miniSDK.UniformMessageSend(ctx, "Openid", body) if err != nil { xlog.Error(err) return } - xlog.Debugf("rsp:%+v", rsp) } func TestVerifyDecryptOpenData(t *testing.T) { diff --git a/sdk_open_test.go b/sdk_open_test.go new file mode 100644 index 0000000..ea23be7 --- /dev/null +++ b/sdk_open_test.go @@ -0,0 +1,16 @@ +package wechat + +import ( + "testing" + + "github.com/go-pay/wechat-sdk/pkg/xlog" +) + +func TestUserInfo(t *testing.T) { + rsp, err := openSDK.UserInfo(ctx, "openid", "zh_CN") + if err != nil { + xlog.Error(err) + return + } + xlog.Infof("rsp:%+v", rsp) +} diff --git a/sdk_public_test.go b/sdk_public_test.go index 0ce592c..13b67b3 100644 --- a/sdk_public_test.go +++ b/sdk_public_test.go @@ -87,10 +87,9 @@ func TestUserTagList(t *testing.T) { } func TestUserTagUpdate(t *testing.T) { - rsp, err := publicSDK.UserTagUpdate(ctx, 100, "test_tag_update") + err = publicSDK.UserTagUpdate(ctx, 100, "test_tag_update") if err != nil { xlog.Error(err) return } - xlog.Infof("rsp:%+v", rsp) } diff --git a/sdk_test.go b/sdk_test.go index fb1fdd0..c394bb8 100644 --- a/sdk_test.go +++ b/sdk_test.go @@ -48,6 +48,7 @@ func TestMain(m *testing.M) { wxsdk.SetMiniOrPublicATCallback(func(accessToken string, expireIn int, err error) { if err != nil { xlog.Errorf("refresh access token error(%+v)", err) + return } xlog.Infof("accessToken: %s", accessToken) xlog.Infof("expireIn: %d", expireIn) @@ -70,13 +71,27 @@ func TestMain(m *testing.M) { publicSDK.DebugSwitch = DebugOff // New 微信开放平台 SDK - //openSDK, err = wxsdk.NewOpen() - //if err != nil { - // xlog.Error(err) - // return - //} - //openSDK.DebugSwitch = DebugOff - + openSDK, err = wxsdk.NewOpen() + if err != nil { + xlog.Error(err) + return + } + openSDK.DebugSwitch = DebugOff + // 注意:必须换取 开放平台 自己的AccessToken,与小程序和公众号不通用 + openAT, err := openSDK.Code2AccessToken(ctx, "xxx") + if err != nil { + xlog.Error(err) + return + } + xlog.Infof("open at: %+v", openAT) + // 每次刷新 accessToken 后,此方法回调返回 accessToken 和 有效时间(秒) + openSDK.SetOpenATCallback(func(at *open.AccessToken, err error) { + if err != nil { + xlog.Errorf("refresh access token error(%+v)", err) + return + } + xlog.Infof("AccessToken: %+v", at) + }) os.Exit(m.Run()) }