wechat.go 4.9 KB

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