| 
					
				 | 
			
			
				@@ -0,0 +1,247 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+package wechat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"encoding/json" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"fmt" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"net/url" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"time" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"git.chuangxin1.com/cx/myth" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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 = postJSONBody(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 = myth.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 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |