Files
mp-qvyun/backend/providers/wechat/wechat.go
2024-11-29 15:38:57 +08:00

183 lines
4.1 KiB
Go

package wechat
import (
"crypto/sha1"
"encoding/hex"
"net/url"
"sort"
"strings"
"github.com/imroc/req/v3"
"github.com/pkg/errors"
)
const BaseURL = "https://api.weixin.qq.com/"
var DefaultClient = req.
NewClient().
SetBaseURL(BaseURL).
SetCommonHeader("Content-Type", "application/json")
const (
ScopeBase = "snsapi_base"
ScopeUserInfo = "snsapi_userinfo"
)
type AuthScope string
func (s AuthScope) String() string {
return string(s)
}
type Client struct {
client *req.Client
appID string
appSecret string
token string
aesKey string
}
func New(options ...Options) *Client {
we := &Client{
client: DefaultClient,
}
for _, opt := range options {
opt(we)
}
return we
}
func (we *Client) Verify(signature, timestamp, nonce string) error {
params := []string{signature, timestamp, nonce, we.token}
sort.Strings(params)
str := strings.Join(params, "")
hash := sha1.Sum([]byte(str))
hashStr := hex.EncodeToString(hash[:])
if hashStr == signature {
return errors.New("Signature verification failed")
}
return nil
}
func (we *Client) wrapParams(params map[string]string) map[string]string {
if params == nil {
params = make(map[string]string)
}
params["appid"] = we.appID
params["secret"] = we.appSecret
return params
}
func (we *Client) GetAccessToken() (*AccessTokenResponse, error) {
params := map[string]string{
"grant_type": "client_credential",
}
var data AccessTokenResponse
_, err := we.client.R().SetSuccessResult(&data).SetQueryParams(params).Get("/cgi-bin/token")
if err != nil {
return nil, errors.Wrap(err, "call /cgi-bin/token failed")
}
return &data, nil
}
// ScopeAuthorizeURL
func (we *Client) ScopeAuthorizeURL(opts ...ScopeAuthorizeURLOptions) (*url.URL, error) {
params := url.Values{}
params.Add("appid", we.appID)
params.Add("response_type", "code")
for _, opt := range opts {
opt(params)
}
if params.Get("scope") == "" {
params.Add("scope", ScopeBase)
}
u, err := url.Parse("https://open.weixin.qq.com/connect/oauth2/authorize")
if err != nil {
return nil, errors.Wrap(err, "parse url failed")
}
u.Fragment = "wechat_redirect"
u.RawQuery = url.Values(params).Encode()
return u, nil
}
type AuthorizeAccessToken struct {
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
IsSnapshotuser int64 `json:"is_snapshotuser"`
Openid string `json:"openid"`
RefreshToken string `json:"refresh_token"`
Scope string `json:"scope"`
Unionid string `json:"unionid"`
}
func (we *Client) AuthorizeCode2Token(code string) (*AuthorizeAccessToken, error) {
params := we.wrapParams(map[string]string{
"code": code,
"grant_type": "authorization_code",
})
var data AuthorizeAccessToken
_, err := we.client.R().SetSuccessResult(&data).SetQueryParams(params).Get("/sns/oauth2/access_token")
if err != nil {
return nil, errors.Wrap(err, "call /sns/oauth2/access_token failed")
}
return &data, nil
}
func (we *Client) AuthorizeRefreshAccessToken(accessToken string) (*AuthorizeAccessToken, error) {
params := we.wrapParams(map[string]string{
"refresh_token": accessToken,
"grant_type": "refresh_token",
})
var data AuthorizeAccessToken
_, err := we.client.R().SetSuccessResult(&data).SetQueryParams(params).Get("/sns/oauth2/refresh_token")
if err != nil {
return nil, errors.Wrap(err, "call /sns/oauth2/refresh_token failed")
}
return &data, nil
}
type AuthorizeUserInfo struct {
City string `json:"city"`
Country string `json:"country"`
Headimgurl string `json:"headimgurl"`
Nickname string `json:"nickname"`
Openid string `json:"openid"`
Privilege []string `json:"privilege"`
Province string `json:"province"`
Sex int64 `json:"sex"`
Unionid string `json:"unionid"`
}
func (we *Client) AuthorizeUserInfo(accessToken, openID string) (*AuthorizeUserInfo, error) {
params := (map[string]string{
"access_token": accessToken,
"openid": openID,
})
var data AuthorizeUserInfo
_, err := we.client.R().SetSuccessResult(&data).SetQueryParams(params).Get("/sns/userinfo")
if err != nil {
return nil, errors.Wrap(err, "call /sns/userinfo failed")
}
return &data, nil
}