package wechat import ( "encoding/json" "fmt" "net/url" "time" "git.chuangxin1.com/cx/util" ) // Client wechat type Client struct { AppID string `json:"appid"` AppSecret string `json:"appsecret"` Token string `json:"token"` EncodingAESKey string `json:"encodingaeskey"` AccessToken string LastTokenTime int64 Ticket string LastTicketTime int64 } // FormAuthorize get code type FormAuthorize struct { Code string `form:"code"` State string `form:"state"` URL string `form:"url"` } // FormOpenID openid type FormOpenID struct { OpenID string `form:"openid"` } // FormMaterial Material type FormMaterial struct { Type string `form:"type"` Offset int `form:"offset"` Count int `form:"count"` } // UserInfo userinfo type UserInfo struct { SubScribe int `json:"subscribe"` OpenID string `json:"openid"` NickName string `json:"nickname"` Sex int `json:"sex"` Language string `json:"language"` City string `json:"city"` Province string `json:"province"` Country string `json:"country"` HeadImgURL string `json:"headimgurl"` SubscribeTime int `json:"subscribe_time"` UnionID string `json:"unionid"` Remark string `json:"remark"` GroupID int `json:"groupid"` TagidList []int `json:"tagid_list"` SubscribeScene string `json:"subscribe_scene"` QrScene int `json:"qr_scene"` QrSceneStr string `json:"qr_scene_str"` } // List openid list type List struct { OpenID []string `json:"openid"` } // UserList user list type UserList struct { Total int `json:"total"` Count int `json:"count"` Data List `json:"data"` NextOpenID string `json:"next_openid"` } type newsitem struct { Title string `json:"title"` Author string `json:"author"` Digest string `json:"digest"` Content string `json:"content"` ContentSourceURL string `json:"content_source_url"` ThumbMediaID string `json:"thumb_media_id"` ShowCoverPic int `json:"show_cover_pic"` URL string `json:"url"` ThumbURL string `json:"thumb_url"` NeedOpenComment int `json:"need_open_comment"` OnlyFansCanComment int `json:"only_fans_can_comment"` } type content struct { NewsItem []*newsitem `json:"news_item,omitempty"` CreateTime int `json:"create_time,omitempty"` UpdateTime int `json:"update_time,omitempty"` } type item struct { MediaID string `json:"media_id"` Name string `json:"name,omitempty"` UpdateTime int `json:"update_time"` URL string `json:"url,omitempty"` Content *content `json:"content,omitempty"` } // Material 素材 type Material struct { TotalCount int `json:"total_count"` ItemCount int `json:"item_count"` Item []item `json:"item"` } // MiniProgramPage mini type MiniProgramPage struct { AppID string `json:"appid,omitempty"` PagePath string `json:"pagepath,omitempty"` } // ValueColor value color type ValueColor struct { Value string `json:"value"` Color string `json:"color,omitempty"` } // TemplateData data type TemplateData struct { First *ValueColor `json:"first"` Keyword1 *ValueColor `json:"keyword1,omitempty"` Keyword2 *ValueColor `json:"keyword2,omitempty"` Keyword3 *ValueColor `json:"keyword3,omitempty"` Keyword4 *ValueColor `json:"keyword4,omitempty"` Keyword5 *ValueColor `json:"keyword5,omitempty"` Remark *ValueColor `json:"remark,omitempty"` } // TemplateMessage template message type TemplateMessage struct { ToUser string `json:"touser"` TemplateID string `json:"template_id"` URL string `json:"url"` MiniProgram *MiniProgramPage `json:"miniprogram,omitempty"` Data TemplateData `json:"data"` } // SignPackage sign package type SignPackage struct { AppID string `json:"appId"` NonceStr string `json:"nonceStr"` Timestamp int64 `json:"timestamp"` Signature string `json:"signature"` URL string `json:"url"` } func key(appid string) string { return "wechat:client:" + appid } // NewClient new client func NewClient(appID, appSecret, token, encodingAESKey string) *Client { key := key(appID) if v, ok := cache.Load(key); ok { return v.(*Client) } c := &Client{AppID: appID, AppSecret: appSecret, Token: token, EncodingAESKey: encodingAESKey} cache.Store(key, c) return c } // getToken get token func (wc *Client) getToken() (token string, err error) { now := time.Now().Unix() if wc.LastTokenTime > 0 { if now-wc.LastTokenTime < TokenExpires { token = wc.AccessToken return } } uri := BaseURL + "/cgi-bin/token?" args := url.Values{} args.Add("grant_type", "client_credential") args.Add("appid", wc.AppID) args.Add("secret", wc.AppSecret) uri += args.Encode() var res Response if res, err = getJSON(uri); err == nil { wc.LastTokenTime = now wc.AccessToken = res.AccessToken token = wc.AccessToken key := key(wc.AppID) cache.Store(key, wc) } return } // GetCodeURL get code 授权获取 OpenID URL // /connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect // 转向到 // redirect_uri/?code=CODE&state=STATE func (wc Client) GetCodeURL(redirectURL, state string) (uri string) { uri = OpenURL + "/connect/oauth2/authorize?" args := url.Values{} args.Add("appid", wc.AppID) args.Add("redirect_uri", redirectURL) args.Add("response_type", "code") args.Add("scope", "snsapi_base") args.Add("state", state) uri += args.Encode() uri += "#wechat_redirect" return } // GetOpenID 获取 OpenID // /sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code func (wc Client) GetOpenID(code string) (res Response, err error) { uri := BaseURL + "/sns/oauth2/access_token?" if wc.AccessToken, err = wc.getToken(); err != nil { return } args := url.Values{} args.Add("access_token", wc.AccessToken) args.Add("appid", wc.AppID) args.Add("secret", wc.AppSecret) args.Add("code", code) args.Add("grant_type", "authorization_code") uri += args.Encode() res, err = getJSON(uri) return } // GetUserInfo user info func (wc Client) GetUserInfo(openid string) (res UserInfo, err error) { uri := BaseURL + "/cgi-bin/user/info?" if wc.AccessToken, err = wc.getToken(); err != nil { return } args := url.Values{} args.Add("access_token", wc.AccessToken) args.Add("openid", openid) args.Add("lang", "zh_CN") uri += args.Encode() var body []byte body, err = getBody(uri) if err == nil { err = json.Unmarshal(body, &res) } return } // GetUserList user list func (wc Client) GetUserList(nextOpenID string) (res UserList, err error) { uri := BaseURL + "/cgi-bin/user/get?" if wc.AccessToken, err = wc.getToken(); err != nil { return } args := url.Values{} args.Add("access_token", wc.AccessToken) args.Add("next_openid", nextOpenID) uri += args.Encode() var body []byte body, err = getBody(uri) if err == nil { err = json.Unmarshal(body, &res) } return } // GetMaterial 永久资料 func (wc Client) GetMaterial(mtype string, offset, count int) (res Material, err error) { uri := BaseURL + "/cgi-bin/material/batchget_material?" if wc.AccessToken, err = wc.getToken(); err != nil { return } args := url.Values{} data := make(map[string]interface{}) args.Add("access_token", wc.AccessToken) data["type"] = mtype data["offset"] = offset data["count"] = count uri += args.Encode() var body []byte params, err := json.Marshal(data) body, err = postBody(uri, params) if err == nil { err = json.Unmarshal(body, &res) } return } func (wc *Client) getTicket(now int64) (ticket string, err error) { var res Response if wc.LastTicketTime > 0 { if now-wc.LastTokenTime < TokenExpires { ticket = wc.Ticket return } } args := url.Values{} args.Add("access_token", wc.AccessToken) args.Add("type", "jsapi") uri := BaseURL + "/cgi-bin/ticket/getticket?" uri += args.Encode() if res, err = getJSON(uri); err == nil { fmt.Println(res) wc.LastTicketTime = now wc.Ticket = res.Ticket key := key(wc.AppID) cache.Store(key, wc) ticket = res.Ticket } return } // GetSignPackage JS 签名 // uri 当前 URL // nonceStr 随机字符串 func (wc Client) GetSignPackage(uri, nonceStr string) (sign SignPackage, err error) { if wc.AccessToken, err = wc.getToken(); err != nil { return } var ( s string unix int64 ) unix = time.Now().Unix() if wc.Ticket, err = wc.getTicket(unix); err != nil { return } //fmt.Println(wc.Ticket) s = `jsapi_ticket=` + wc.Ticket s += `&noncestr=` + nonceStr s += `×tamp=` + fmt.Sprintf("%d", unix) s += `&url=` + uri //fmt.Println(s) sign.AppID = wc.AppID sign.NonceStr = nonceStr sign.Signature = util.SHA1(s) sign.Timestamp = unix sign.URL = uri return } // SendTemplateMessage send template message // POST /cgi-bin/message/template/send?access_token=ACCESS_TOKEN func (wc Client) SendTemplateMessage(template TemplateMessage) (res Response, err error) { uri := BaseURL + "/cgi-bin/message/template/send?" if wc.AccessToken, err = wc.getToken(); err != nil { return } args := url.Values{} args.Add("access_token", wc.AccessToken) uri += args.Encode() data, err := json.Marshal(template) if err != nil { return } res, err = postJSON(uri, data) return }