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)) }