Browse Source

wechat echo

ls 5 years ago
parent
commit
e6a04e987d
6 changed files with 305 additions and 0 deletions
  1. 31 0
      wechat/const.go
  2. 110 0
      wechat/request.go
  3. 7 0
      wechat/response.go
  4. 28 0
      wechat/server.go
  5. 59 0
      wechat/sign.go
  6. 70 0
      wechat/structure.go

+ 31 - 0
wechat/const.go

@@ -0,0 +1,31 @@
+package wechat
+
+const (
+	// TokenExpires token expires time 1 hours
+	TokenExpires = 60 * 60
+	// BaseURL api host
+	BaseURL = `https://api.weixin.qq.com`
+	// OpenURL open host
+	OpenURL = `https://open.weixin.qq.com`
+
+	// PayHost pay base host
+	PayHost = `https://api.mch.weixin.qq.com`
+
+	// PayURLUnifiedOrder pay 付款
+	PayURLUnifiedOrder = `/pay/unifiedorder`
+	// PayURLUnifiedQuery order query 订单查询
+	PayURLUnifiedQuery = `/pay/orderquery`
+	// PayURLPayRefund pay refund 退款
+	PayURLPayRefund = `/secapi/pay/refund`
+	// PayURLPapPay 委托代扣申请扣款
+	PayURLPapPay = `/pay/pappayapply`
+	// PayURLPapayEntrust H5 纯签约
+	PayURLPapayEntrust = `/papay/h5entrustweb`
+
+	// PayTradeTypeJS JSAPI 公众号支付
+	PayTradeTypeJS = `JSAPI`
+	// PayTradeTypeNative NATIVE 扫码支付
+	PayTradeTypeNative = `NATIVE`
+	// PayTradeTypeAPP APP APP支付
+	PayTradeTypeAPP = `APP`
+)

+ 110 - 0
wechat/request.go

@@ -0,0 +1,110 @@
+package wechat
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+
+	"git.chuangxin1.com/cx/myth"
+)
+
+func checkErrorWithBody(msg myth.HTTPMessage) (body []byte, err error) {
+	if msg.StatusCode == 200 {
+		var res ResponseMsg
+		err = json.Unmarshal(msg.Body, &res)
+		if err == nil {
+			if res.ErrCode != 0 {
+				err = errors.New(res.ErrMsg)
+			} else {
+				body = msg.Body
+			}
+		}
+	} else {
+		err = errors.New(`HTTP StatusCode not 200`)
+	}
+	return
+}
+
+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 {
+				err = errors.New(res.ErrMsg)
+			}
+		}
+	} else {
+		err = errors.New(`HTTP StatusCode not 200`)
+	}
+	return
+}
+
+func getJSON(uri string) (res Response, err error) {
+	var msg myth.HTTPMessage
+	if msg, err = myth.Get(uri, "", "", map[string]string{}); err != nil {
+		return
+	}
+	res, err = checkError(msg)
+	return
+}
+
+func getBody(uri string) (body []byte, err error) {
+	var msg myth.HTTPMessage
+	if msg, err = myth.Get(uri, "", "", map[string]string{}); err != nil {
+		return
+	}
+	body, err = checkErrorWithBody(msg)
+	return
+}
+
+func postXML(url string, data []byte) (res Response, err error) {
+	var (
+		msg     myth.HTTPMessage
+		headers = make(map[string]string)
+	)
+
+	headers["Accept-Encoding"] = "gzip, deflate, br"
+	headers["Accept"] = "application/json"
+	headers["Content-Type"] = "application/xml; charset=utf-8"
+	msg, err = myth.Post(url, "", "", headers, bytes.NewReader(data))
+	if err != nil {
+		return
+	}
+	res, err = checkError(msg)
+	return
+}
+
+func postJSON(url string, data []byte) (res Response, err error) {
+	var (
+		msg     myth.HTTPMessage
+		headers = make(map[string]string)
+	)
+
+	headers["Accept-Encoding"] = "gzip, deflate, br"
+	headers["Accept"] = "application/json"
+	headers["Content-Type"] = "application/json; charset=utf-8"
+	msg, err = myth.Post(url, "", "", headers, bytes.NewReader(data))
+	if err != nil {
+		return
+	}
+	res, err = checkError(msg)
+	return
+}
+
+func postJSONBody(uri string, data []byte) (body []byte, err error) {
+	var (
+		msg     myth.HTTPMessage
+		headers = make(map[string]string)
+	)
+
+	headers["Accept-Encoding"] = "gzip, deflate, br"
+	headers["Accept"] = "application/json"
+	headers["Content-Type"] = "application/json; charset=utf-8"
+	msg, err = myth.Post(uri, "", "", headers, bytes.NewReader(data))
+	if err != nil {
+		return
+	}
+	body, err = checkErrorWithBody(msg)
+
+	return
+}

+ 7 - 0
wechat/response.go

@@ -0,0 +1,7 @@
+package wechat
+
+import "sync"
+
+var (
+	cache sync.Map
+)

+ 28 - 0
wechat/server.go

@@ -0,0 +1,28 @@
+package wechat
+
+// Server wechat
+type Server struct {
+	AppID          string `json:"appid"`
+	AppSecret      string `json:"appsecret"`
+	Token          string `json:"token"`
+	EncodingAESKey string `json:"encodingaeskey"`
+}
+
+// NewServer new Server
+func NewServer(appID, appSecret, token, encodingAESKey string) *Server {
+	key := "wechat:server:" + appID
+	if v, ok := cache.Load(key); ok {
+		return v.(*Server)
+	}
+	s := &Server{AppID: appID, AppSecret: appSecret, Token: token, EncodingAESKey: encodingAESKey}
+	cache.Store(key, s)
+	return s
+}
+
+// Echo notify echo GET
+func (wc Server) Echo(s FormSignature) (data string, err error) {
+	if ok := makeSignature(wc.Token, s.Signature, s.TimeStamp, s.Nonce); ok {
+		data = s.Echostr
+	}
+	return
+}

+ 59 - 0
wechat/sign.go

@@ -0,0 +1,59 @@
+package wechat
+
+import (
+	"fmt"
+	"math/rand"
+	"sort"
+	"strings"
+	"time"
+
+	"git.chuangxin1.com/cx/myth"
+)
+
+// Sign 微信计算签名的函数
+func Sign(req map[string]interface{}, key string) (sign string) {
+	keys := make([]string, 0)
+
+	for k := range req {
+		keys = append(keys, k)
+	}
+
+	sort.Strings(keys)
+
+	var s string
+	for _, k := range keys {
+		value := fmt.Sprintf("%v", req[k])
+		if value != "" {
+			s = s + k + "=" + value + "&"
+		}
+	}
+
+	if key != "" {
+		s = s + "key=" + key
+	}
+
+	sign = strings.ToUpper(myth.MD5(s))
+
+	return
+}
+
+// Random 指定长度的随机字符串(字母或数字)
+func Random(n int) string {
+	str := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+	bytes := []byte(str)
+	result := []byte{}
+
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	n1 := len(bytes)
+	for i := 0; i < n; i++ {
+		result = append(result, bytes[r.Intn(n1)])
+	}
+	return string(result)
+}
+
+// makeSignature echo 签名验证
+func makeSignature(token, signature, timestamp, nonce string) bool {
+	sl := []string{token, timestamp, nonce}
+	sort.Strings(sl)
+	return strings.Compare(signature, myth.SHA1(strings.Join(sl, ""))) == 0
+}

+ 70 - 0
wechat/structure.go

@@ -0,0 +1,70 @@
+package wechat
+
+import "encoding/xml"
+
+// FormSignature signature
+type FormSignature struct {
+	TimeStamp string `form:"timestamp" json:"timestamp"`
+	Nonce     string `form:"nonce" json:"nonce"`
+	Signature string `form:"signature" json:"signature"`
+	Echostr   string `form:"echostr" json:"echostr"`
+}
+
+// ResponseMsg response
+type ResponseMsg struct {
+	ErrCode int    `json:"errcode"`
+	ErrMsg  string `json:"errmsg"`
+}
+
+// Response response
+type Response struct {
+	ErrCode int    `json:"errcode"`
+	ErrMsg  string `json:"errmsg"`
+
+	// token
+	AccessToken string `json:"access_token"`
+	ExpiresIn   int    `json:"expires_in"`
+
+	// openid
+	RefreshToken string `json:"refresh_token"`
+	OpenID       string `json:"openid"`
+	Scope        string `json:"scope"`
+
+	// user info
+	NickName   string `json:"nickname"`
+	Sex        string `json:"sex"`
+	Province   string `json:"province"`
+	City       string `json:"city"`
+	Country    string `json:"country"`
+	HeadImgURL string `json:"headimgurl"`
+	Privilege  string `json:"privilege"`
+	UnionID    string `json:"unionid"`
+
+	// template message
+	MsgID int64 `json:"msgid"`
+
+	// jsapi_ticket
+	Ticket string `json:"ticket"`
+}
+
+// Message message
+type Message struct {
+	XMLName      xml.Name `xml:"xml"`
+	ToUserName   string   `xml:"ToUserName" json:"ToUserName"`
+	FromUserName string   `xml:"FromUserName" json:"FromUserName"`
+	CreateTime   int32    `xml:"CreateTime" json:"CreateTime"`
+	MsgType      string   `xml:"MsgType" json:"MsgType"`
+	MsgID        int64    `xml:"MsgId" json:"MsgId"`
+}
+
+// EventTemplateReply event reply
+type EventTemplateReply struct {
+	XMLName      xml.Name `xml:"xml"`
+	ToUserName   string   `xml:"ToUserName"`
+	FromUserName string   `xml:"FromUserName"`
+	CreateTime   string   `xml:"CreateTime"`
+	MsgType      string   `xml:"MsgType"`
+	Event        string   `xml:"Event"`
+	MsgID        string   `xml:"MsgID"`
+	Status       string   `xml:"Status"`
+}