You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
3.8 KiB
Go

package utils
import (
"crypto/sha1"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"miniapp/global"
"net/http"
"net/url"
"sort"
"strings"
"sync"
"time"
)
//type wxMsg struct{}
//
//func WxMsgUtils() *wxMsg {
// return &wxMsg{}
//}
type Wechat struct {
appID string
secret string
templateID string
accessToken *AccessToken
sync.Mutex
}
type OpenIDResponse struct {
Openid string `json:"openid"`
SessionKey string `json:"session_key"`
Unionid string `json:"unionid"`
WxErr
}
type CheckTokenRequest struct {
Signature string `json:"signature"`
Timestamp string `json:"timestamp"`
Nonce string `json:"nonce"`
Echostr string `json:"echostr"`
}
type WxErr struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
type AccessToken struct {
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
WxErr
}
type SendRequest struct {
Touser string `json:"touser"`
Page string `json:"page"`
Data Message `json:"data"`
EmphasisKeyword string `json:"emphasis_keyword"`
}
type Message map[string]interface{}
func (w *Wechat) SendMsg(req SendRequest) (err error) {
token, err := w.GetAccessToken()
if err != nil {
return
}
api, err := TokenAPI("https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send", token)
if err != nil {
return
}
for key := range req.Data {
req.Data[key] = Message{"value": req.Data[key]}
}
body := map[string]interface{}{
"touser": "o9Fq_6_cYKvOWnyUM3McC11hWsTI",
"template_id": global.GVA_CONFIG.MiniApp.TemplateID,
"page": "/pages/index/todo",
"data": req.Data,
"emphasis_keyword": req.EmphasisKeyword,
}
b, err := json.Marshal(body)
if err != nil {
return err
}
res, err := http.Post(api, "application/json", strings.NewReader(string(b)))
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != 200 {
err = errors.New("WECHAT_SERVER_ERROR")
return err
}
var resp WxErr
if err = json.NewDecoder(res.Body).Decode(&resp); err != nil {
return err
}
if resp.Errcode != 0 {
return errors.New(resp.Errmsg)
}
return nil
}
func (w *Wechat) GetAccessToken() (token string, err error) {
w.Lock()
defer w.Unlock()
if w.accessToken == nil || w.accessToken.ExpiresIn < time.Now().Unix() {
for i := 0; i < 3; i++ {
err = w.getAccessToken()
if err == nil {
break
}
time.Sleep(time.Second)
}
if err != nil {
return
}
}
token = w.accessToken.AccessToken
return
}
func (w *Wechat) CheckSignature(req CheckTokenRequest) (err error) {
if sig := w.sortSha1(req.Timestamp, req.Nonce, req.Echostr); sig != req.Signature {
err = errors.New("check signature failed.")
return
}
return
}
func TokenAPI(api, token string) (string, error) {
u, err := url.Parse(api)
if err != nil {
return "", err
}
query := u.Query()
query.Set("access_token", token)
u.RawQuery = query.Encode()
return u.String(), nil
}
func (w *Wechat) getAccessToken() (err error) {
urls, err := url.Parse("https://api.weixin.qq.com/cgi-bin/token")
if err != nil {
return
}
query := urls.Query()
query.Set("appid", w.appID)
query.Set("secret", w.secret)
query.Set("grant_type", "client_credential")
urls.RawQuery = query.Encode()
res, err := http.Get(urls.String())
if err != nil {
return
}
defer res.Body.Close()
if res.StatusCode != 200 {
return errors.New("wechat internal server error.")
}
var token AccessToken
if err = json.NewDecoder(res.Body).Decode(&token); err != nil {
return
}
if token.Errcode != 0 {
return errors.New(token.Errmsg)
}
w.accessToken.AccessToken = token.AccessToken
w.accessToken.ExpiresIn = token.ExpiresIn
return
}
func (w *Wechat) sortSha1(s ...string) string {
sort.Strings(s)
h := sha1.New()
h.Write([]byte(strings.Join(s, "")))
return fmt.Sprintf("%x", h.Sum(nil))
}