wechat.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. package wechat
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "encoding/xml"
  6. "errors"
  7. "fmt"
  8. "math/rand"
  9. "sort"
  10. "strings"
  11. "sync"
  12. "time"
  13. "git.chuangxin1.com/cx/util"
  14. )
  15. const (
  16. // TokenExpires token expires time 1 hours
  17. TokenExpires = 60 * 60
  18. // BaseURL api host
  19. BaseURL = `https://api.weixin.qq.com`
  20. // OpenURL open host
  21. OpenURL = `https://open.weixin.qq.com`
  22. )
  23. // ResponseMsg response
  24. type ResponseMsg struct {
  25. ErrCode int `json:"errcode"`
  26. ErrMsg string `json:"errmsg"`
  27. }
  28. // Response response
  29. type Response struct {
  30. ErrCode int `json:"errcode"`
  31. ErrMsg string `json:"errmsg"`
  32. // token
  33. AccessToken string `json:"access_token"`
  34. ExpiresIn int `json:"expires_in"`
  35. // openid
  36. RefreshToken string `json:"refresh_token"`
  37. OpenID string `json:"openid"`
  38. Scope string `json:"scope"`
  39. // user info
  40. NickName string `json:"nickname"`
  41. Sex string `json:"sex"`
  42. Province string `json:"province"`
  43. City string `json:"city"`
  44. Country string `json:"country"`
  45. HeadImgURL string `json:"headimgurl"`
  46. Privilege string `json:"privilege"`
  47. UnionID string `json:"unionid"`
  48. // template message
  49. MsgID int64 `json:"msgid"`
  50. // jsapi_ticket
  51. Ticket string `json:"ticket"`
  52. }
  53. // FormSignature signature
  54. type FormSignature struct {
  55. TimeStamp string `form:"timestamp" json:"timestamp"`
  56. Nonce string `form:"nonce" json:"nonce"`
  57. Signature string `form:"signature" json:"signature"`
  58. Echostr string `form:"echostr" json:"echostr"`
  59. }
  60. // Message message
  61. type Message struct {
  62. XMLName xml.Name `xml:"xml"`
  63. ToUserName string `xml:"ToUserName" json:"ToUserName"`
  64. FromUserName string `xml:"FromUserName" json:"FromUserName"`
  65. CreateTime int32 `xml:"CreateTime" json:"CreateTime"`
  66. MsgType string `xml:"MsgType" json:"MsgType"`
  67. MsgID int64 `xml:"MsgId" json:"MsgId"`
  68. }
  69. // EventTemplateReply event reply
  70. type EventTemplateReply struct {
  71. XMLName xml.Name `xml:"xml"`
  72. ToUserName string `xml:"ToUserName"`
  73. FromUserName string `xml:"FromUserName"`
  74. CreateTime string `xml:"CreateTime"`
  75. MsgType string `xml:"MsgType"`
  76. Event string `xml:"Event"`
  77. MsgID string `xml:"MsgID"`
  78. Status string `xml:"Status"`
  79. }
  80. var (
  81. cache sync.Map
  82. )
  83. // Sign 微信计算签名的函数
  84. func Sign(req map[string]interface{}, key string) (sign string) {
  85. keys := make([]string, 0)
  86. for k := range req {
  87. keys = append(keys, k)
  88. }
  89. sort.Strings(keys)
  90. var s string
  91. for _, k := range keys {
  92. value := fmt.Sprintf("%v", req[k])
  93. if value != "" {
  94. s = s + k + "=" + value + "&"
  95. }
  96. }
  97. if key != "" {
  98. s = s + "key=" + key
  99. }
  100. sign = strings.ToUpper(util.MD5(s))
  101. return
  102. }
  103. // Random 指定长度的随机字符串(字母或数字)
  104. func Random(n int) string {
  105. str := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  106. bytes := []byte(str)
  107. result := []byte{}
  108. r := rand.New(rand.NewSource(time.Now().UnixNano()))
  109. n1 := len(bytes)
  110. for i := 0; i < n; i++ {
  111. result = append(result, bytes[r.Intn(n1)])
  112. }
  113. return string(result)
  114. }
  115. func makeSignature(token, signature, timestamp, nonce string) bool {
  116. sl := []string{token, timestamp, nonce}
  117. sort.Strings(sl)
  118. return strings.Compare(signature, util.SHA1(strings.Join(sl, ""))) == 0
  119. }
  120. func checkErrorWithBody(msg util.Message) (body []byte, err error) {
  121. if msg.StatusCode == 200 {
  122. var res ResponseMsg
  123. err = json.Unmarshal(msg.Body, &res)
  124. if err == nil {
  125. if res.ErrCode != 0 {
  126. err = errors.New(res.ErrMsg)
  127. } else {
  128. body = msg.Body
  129. }
  130. }
  131. } else {
  132. err = errors.New(`HTTP StatusCode not 200`)
  133. }
  134. return
  135. }
  136. func checkError(msg util.Message) (res Response, err error) {
  137. if msg.StatusCode == 200 {
  138. err = json.Unmarshal(msg.Body, &res)
  139. if err == nil {
  140. if res.ErrCode != 0 {
  141. err = errors.New(res.ErrMsg)
  142. }
  143. }
  144. } else {
  145. err = errors.New(`HTTP StatusCode not 200`)
  146. }
  147. return
  148. }
  149. func getJSON(uri string) (res Response, err error) {
  150. var msg util.Message
  151. if msg, err = util.Get(uri, "", "", map[string]string{}); err != nil {
  152. return
  153. }
  154. res, err = checkError(msg)
  155. return
  156. }
  157. func getBody(uri string) (body []byte, err error) {
  158. var msg util.Message
  159. if msg, err = util.Get(uri, "", "", map[string]string{}); err != nil {
  160. return
  161. }
  162. body, err = checkErrorWithBody(msg)
  163. return
  164. }
  165. func postXML(url string, data []byte) (res Response, err error) {
  166. var (
  167. msg util.Message
  168. headers = make(map[string]string)
  169. )
  170. headers["Accept"] = "application/json"
  171. headers["Content-Type"] = "application/xml; charset=utf-8"
  172. msg, err = util.Post(url, "", "", headers, bytes.NewReader(data))
  173. if err != nil {
  174. return
  175. }
  176. res, err = checkError(msg)
  177. return
  178. }
  179. func postJSON(url string, data []byte) (res Response, err error) {
  180. var (
  181. msg util.Message
  182. headers = make(map[string]string)
  183. )
  184. headers["Accept"] = "application/json"
  185. headers["Content-Type"] = "application/json; charset=utf-8"
  186. msg, err = util.Post(url, "", "", headers, bytes.NewReader(data))
  187. if err != nil {
  188. return
  189. }
  190. res, err = checkError(msg)
  191. return
  192. }
  193. func postBody(uri string, data []byte) (body []byte, err error) {
  194. var (
  195. msg util.Message
  196. headers = make(map[string]string)
  197. )
  198. headers["Accept"] = "application/json"
  199. headers["Content-Type"] = "application/json; charset=utf-8"
  200. msg, err = util.Post(uri, "", "", headers, bytes.NewReader(data))
  201. if err != nil {
  202. return
  203. }
  204. body, err = checkErrorWithBody(msg)
  205. return
  206. }