فهرست منبع

wechat mini get openid by code

ls 5 سال پیش
والد
کامیت
322e5bbde5
5فایلهای تغییر یافته به همراه455 افزوده شده و 2 حذف شده
  1. 247 0
      wechat/client.go
  2. 7 0
      wechat/const.go
  3. 38 0
      wechat/mini.go
  4. 2 2
      wechat/request.go
  5. 161 0
      wechat/structure.go

+ 247 - 0
wechat/client.go

@@ -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 += `&timestamp=` + 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
+}

+ 7 - 0
wechat/const.go

@@ -3,6 +3,7 @@ package wechat
 const (
 	// TokenExpires token expires time 1 hours
 	TokenExpires = 60 * 60
+
 	// BaseURL api host
 	BaseURL = `https://api.weixin.qq.com`
 	// OpenURL open host
@@ -11,6 +12,9 @@ const (
 	// PayHost pay base host
 	PayHost = `https://api.mch.weixin.qq.com`
 
+	// ErrReqOk request ok
+	ErrReqOk = 0
+
 	// PayURLUnifiedOrder pay 付款
 	PayURLUnifiedOrder = `/pay/unifiedorder`
 	// PayURLUnifiedQuery order query 订单查询
@@ -28,4 +32,7 @@ const (
 	PayTradeTypeNative = `NATIVE`
 	// PayTradeTypeAPP APP APP支付
 	PayTradeTypeAPP = `APP`
+
+	// MiniURLOpenID 小程序获取 OpenID
+	MiniURLOpenID = `/sns/jscode2session`
 )

+ 38 - 0
wechat/mini.go

@@ -0,0 +1,38 @@
+package wechat
+
+import (
+	"net/url"
+)
+
+func keyMini(appid string) string {
+	return "wechat:mini:" + appid
+}
+
+// NewMiniClient new mini client
+func NewMiniClient(appID, appSecret string) *MiniClient {
+	key := keyMini(appID)
+	if v, ok := cache.Load(key); ok {
+		return v.(*MiniClient)
+	}
+	c := &MiniClient{AppID: appID, AppSecret: appSecret}
+	cache.Store(key, c)
+	return c
+}
+
+// GetMiniOpenID get openid by code
+func (wc *Client) GetMiniOpenID(frm FormCode) (openid string, err error) {
+	uri := BaseURL + "/sns/jscode2session?"
+
+	args := url.Values{}
+	args.Add("grant_type", "authorization_code")
+	args.Add("appid", wc.AppID)
+	args.Add("secret", wc.AppSecret)
+	args.Add("js_code", frm.Code)
+
+	uri += args.Encode()
+	var res Response
+	if res, err = getJSON(uri); err == nil {
+		openid = res.OpenID
+	}
+	return
+}

+ 2 - 2
wechat/request.go

@@ -13,7 +13,7 @@ func checkErrorWithBody(msg myth.HTTPMessage) (body []byte, err error) {
 		var res ResponseMsg
 		err = json.Unmarshal(msg.Body, &res)
 		if err == nil {
-			if res.ErrCode != 0 {
+			if res.ErrCode != ErrReqOk {
 				err = errors.New(res.ErrMsg)
 			} else {
 				body = msg.Body
@@ -29,7 +29,7 @@ func checkError(msg myth.HTTPMessage) (res Response, err error) {
 	if msg.StatusCode == 200 {
 		err = json.Unmarshal(msg.Body, &res)
 		if err == nil {
-			if res.ErrCode != 0 {
+			if res.ErrCode != ErrReqOk {
 				err = errors.New(res.ErrMsg)
 			}
 		}

+ 161 - 0
wechat/structure.go

@@ -10,6 +10,23 @@ type FormSignature struct {
 	Echostr   string `form:"echostr" json:"echostr"`
 }
 
+// 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"`
+}
+
+// FormCode code
+type FormCode struct {
+	Code string `form:"code"`
+}
+
 // ResponseMsg response
 type ResponseMsg struct {
 	ErrCode int    `json:"errcode"`
@@ -45,6 +62,9 @@ type Response struct {
 
 	// jsapi_ticket
 	Ticket string `json:"ticket"`
+
+	// mini get openid by code
+	SessionKey string `json:"session_key"`
 }
 
 // Message message
@@ -68,3 +88,144 @@ type EventTemplateReply struct {
 	MsgID        string   `xml:"MsgID"`
 	Status       string   `xml:"Status"`
 }
+
+// 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"`
+}
+
+// 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
+}
+
+// MiniClient wechat mini
+type MiniClient struct {
+	AppID     string `json:"appid"`
+	AppSecret string `json:"appsecret"`
+
+	AccessToken   string
+	LastTokenTime int64
+}
+
+// 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"`
+	Keyword6 *ValueColor `json:"keyword6,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"`
+}