package wechat import ( "encoding/json" "errors" "fmt" "net/url" "time" "git.chuangxin1.com/cx/myth/cache" "git.chuangxin1.com/cx/myth" ) func keyC(appid string) string { return "wechat:client:" + appid } func keyToken(appid string) string { return "wechat:client:token:" + appid } func keyTicket(appid string) string { return "wechat:client:ticket:" + appid } // NewClient new client func NewClient(appID, appSecret, token, encodingAESKey string) *Client { key := keyC(appID) if v, ok := memcache.Load(key); ok { return v.(*Client) } c := &Client{AppID: appID, AppSecret: appSecret, Token: token, EncodingAESKey: encodingAESKey, UseCacheToken: true} memcache.Store(key, c) return c } // getToken get token func (wc *Client) getToken() (token string, err error) { key := keyToken(wc.AppID) if wc.UseCacheToken { var ct ClientToken s := `` s, err = cache.Get(key) if err != nil { return } err = json.Unmarshal([]byte(s), &ct) if err != nil { return } token = ct.AccessToken return } //wc.ReadLock.Lock() //defer wc.ReadLock.Unlock() now := time.Now().Unix() if wc.LastTokenTime > 0 { if now-wc.LastTokenTime < TokenExpires { token = wc.AccessToken return } } token, err = wc.upToken(key, now) return } func (wc *Client) upToken(key string, now int64) (token string, err error) { 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 jq *myth.JSONQuery if jq, err = getJSON(uri); err == nil { wc.LastTokenTime = now var accessToken string accessToken, err = jq.String(`access_token`) wc.AccessToken = accessToken token = wc.AccessToken key1 := keyC(wc.AppID) memcache.Store(key1, wc) ct := ClientToken{AppID: wc.AppID, AccessToken: wc.AccessToken, LastTokenTime: now} bs, _ := json.Marshal(ct) cache.Set(key, string(bs), 0) //cache.Store(key, accessToken) //log.Println("upToken", key, string(bs)) } return } // 更新公众号 access_token func (wc *Client) UpdateToken() (token string, err error) { key := keyToken(wc.AppID) token, err = wc.upToken(key, time.Now().Unix()) return } func (wc *Client) getTicket(now int64) (ticket string, err error) { key := keyTicket(wc.AppID) if wc.UseCacheToken { var ct ClientTicket s := `` s, err = cache.Get(key) if err != nil { return } err = json.Unmarshal([]byte(s), &ct) if err != nil { return } ticket = ct.Ticket return } //now := time.Now().Unix() var jq *myth.JSONQuery if wc.LastTicketTime > 0 { if now-wc.LastTicketTime < TicketExpires { 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 jq, err = getJSON(uri); err == nil { ticket, err = jq.String(`ticket`) wc.LastTicketTime = now wc.Ticket = ticket key := keyC(wc.AppID) memcache.Store(key, wc) var ct ClientTicket ct.AppID = wc.AppID ct.Ticket = ticket ct.LastTicketTime = now bs, _ := json.Marshal(ct) cache.Set(key, string(bs), 0) } return } func (wc *Client) upTicket(now int64) (ticket string, err error) { key := keyTicket(wc.AppID) var jq *myth.JSONQuery args := url.Values{} args.Add("access_token", wc.AccessToken) args.Add("type", "jsapi") uri := BaseURL + "/cgi-bin/ticket/getticket?" uri += args.Encode() if jq, err = getJSON(uri); err == nil { ticket, err = jq.String(`ticket`) wc.LastTicketTime = now wc.Ticket = ticket var ct ClientTicket ct.AppID = wc.AppID ct.LastTicketTime = now ct.Ticket = ticket bs, _ := json.Marshal(ct) cache.Set(key, string(bs), 0) //log.Println("upTicket", key, string(bs)) } return } // 更新公众号 js ticket func (wc *Client) UpdateTicket() (ticket string, err error) { ticket, err = wc.upTicket(time.Now().Unix()) return } // TokenUseCache token from cache func (wc *Client) TokenUseCache(ok bool) { wc.UseCacheToken = ok wc.TokenFromCache = ok } // GetCodeURL get code 授权获取 OpenID URL(静默授权) // /connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&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 } // GetUserInfoCodeURL get code 授权获取 OpenID URL // /connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect // 转向到 // redirect_uri/?code=CODE&state=STATE func (wc Client) GetUserInfoCodeURL(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_userinfo") 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) (jq *myth.JSONQuery, 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() jq, err = getJSON(uri) return } // GetUserInfo user info func (wc Client) GetUserInfo(openid string) (info 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 msg myth.HTTPMessage msg, err = getBody(uri) if err == nil { err = msg.JSON(&info) } return } // GetUserList user list func (wc Client) GetUserList(nextOpenID string) (list 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 msg myth.HTTPMessage msg, err = getBody(uri) if err == nil { err = msg.JSON(&list) } return } // GetMaterial 永久资料 func (wc Client) GetMaterial(mtype string, offset, count int) (material 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 msg myth.HTTPMessage msg, err = postJSON(uri, data) if err == nil { err = msg.JSON(&material) } return } // GetMenu 查询自定义菜单 func (wc Client) GetMenu() (menu Menu, err error) { uri := BaseURL + "/cgi-bin/get_current_selfmenu_info?" if wc.AccessToken, err = wc.getToken(); err != nil { return } args := url.Values{} data := make(map[string]interface{}) args.Add("access_token", wc.AccessToken) uri += args.Encode() var msg myth.HTTPMessage msg, err = postJSON(uri, data) if err == nil { err = msg.JSON(&menu) } return } // CreateMenu 创建自定义菜单 func (wc Client) CreateMenu(menu FormMenu) (err error) { uri := BaseURL + "/cgi-bin/menu/create?" if wc.AccessToken, err = wc.getToken(); err != nil { return } args := url.Values{} args.Add("access_token", wc.AccessToken) uri += args.Encode() var msg myth.HTTPMessage msg, err = postJSON(uri, menu) if err != nil { return } jq, e := msg.JSONQuery() if e != nil { err = e return } code, _ := jq.Int(`errcode`) errmsg, _ := jq.String(`errmsg`) if code != ErrReqOk { err = errors.New(errmsg) } 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 } s = `jsapi_ticket=` + wc.Ticket s += `&noncestr=` + nonceStr s += `×tamp=` + fmt.Sprintf("%d", unix) s += `&url=` + uri sign.AppID = wc.AppID sign.NonceStr = nonceStr sign.Signature = myth.SHA1(s) sign.Timestamp = unix sign.Ticket = wc.Ticket 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 TemplateResponse, 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() var msg myth.HTTPMessage msg, err = postJSON(uri, template) if err == nil { err = json.Unmarshal(msg.Body, &res) } return } // SendTemplatex send template message // POST /cgi-bin/message/template/send?access_token=ACCESS_TOKEN func (wc Client) SendTemplatex(data any) (res TemplateResponse, 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() var msg myth.HTTPMessage msg, err = postJSON(uri, data) if err == nil { err = json.Unmarshal(msg.Body, &res) } return } // TemplateMessageReslut template message reslut func (wc Client) TemplateMessageReslut(jq *myth.JSONQuery) (code int, id int64, err error) { code, _ = jq.Int(`errcode`) errmsg, _ := jq.String(`errmsg`) id, _ = jq.Int64(`msgid`) if code != ErrReqOk { err = errors.New(errmsg) } return } // SendTemplateMessageX 发送自定义模版消息 func (wc Client) SendTemplateMessageX(data []byte) (res TemplateResponse, 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() var msg myth.HTTPMessage msg, err = postJSONData(uri, data) if err == nil { err = json.Unmarshal(msg.Body, &res) } return }