scrm-go

open_wechat_service.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. package wechat_service
  2. import (
  3. "bytes"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/rand"
  7. "crypto/sha1"
  8. "encoding/base64"
  9. "encoding/binary"
  10. "encoding/json"
  11. "encoding/xml"
  12. "errors"
  13. "fmt"
  14. "io"
  15. "io/ioutil"
  16. math_rand "math/rand"
  17. "net/http"
  18. "sort"
  19. "strings"
  20. "time"
  21. "SCRM/service"
  22. "SCRM/utils"
  23. "github.com/astaxie/beego"
  24. "github.com/astaxie/beego/context"
  25. "github.com/astaxie/beego/httplib"
  26. "github.com/silenceper/wechat/util"
  27. )
  28. func init() {
  29. AesKey = EncodingAESKey2AESKey(encodingAESKey)
  30. }
  31. var (
  32. //以下均为公众号管理后台设置项
  33. token = beego.AppConfig.String("openwechattoken")
  34. appID = beego.AppConfig.String("openwechatappid")
  35. encodingAESKey = beego.AppConfig.String("openwechatencodingaeskey")
  36. )
  37. func MakeMsgSignature(timestamp, nonce, msg_encrypt string) string {
  38. sl := []string{token, timestamp, nonce, msg_encrypt}
  39. sort.Strings(sl)
  40. s := sha1.New()
  41. io.WriteString(s, strings.Join(sl, ""))
  42. return fmt.Sprintf("%x", s.Sum(nil))
  43. }
  44. func ValidateMsg(timestamp, nonce, msgEncrypt, msgSignatureIn string) bool {
  45. msgSignatureGen := MakeMsgSignature(timestamp, nonce, msgEncrypt)
  46. if msgSignatureGen != msgSignatureIn {
  47. return false
  48. }
  49. return true
  50. }
  51. func AesDecrypt(cipherData []byte, aesKey []byte) ([]byte, error) {
  52. k := len(aesKey) //PKCS#7
  53. if len(cipherData)%k != 0 {
  54. return nil, errors.New("crypto/cipher: ciphertext size is not multiple of aes key length")
  55. }
  56. block, err := aes.NewCipher(aesKey)
  57. if err != nil {
  58. return nil, err
  59. }
  60. iv := make([]byte, aes.BlockSize)
  61. if _, err := io.ReadFull(rand.Reader, iv); err != nil {
  62. return nil, err
  63. }
  64. blockMode := cipher.NewCBCDecrypter(block, iv)
  65. plainData := make([]byte, len(cipherData))
  66. blockMode.CryptBlocks(plainData, cipherData)
  67. return plainData, nil
  68. }
  69. var AesKey []byte
  70. func EncodingAESKey2AESKey(encodingKey string) []byte {
  71. data, _ := base64.StdEncoding.DecodeString(encodingKey + "=")
  72. return data
  73. }
  74. func ValidateAppId(id []byte) bool {
  75. if string(id) == appID {
  76. return true
  77. }
  78. return false
  79. }
  80. func ParseEncryptTextRequestBody(plainText []byte) (*TextRequestBody, error) {
  81. // Read length
  82. buf := bytes.NewBuffer(plainText[16:20])
  83. var length int32
  84. binary.Read(buf, binary.BigEndian, &length)
  85. // appID validation
  86. appIDstart := 20 + length
  87. id := plainText[appIDstart : int(appIDstart)+len(appID)]
  88. if !ValidateAppId(id) {
  89. return nil, errors.New("Appid is invalid")
  90. }
  91. textRequestBody := &TextRequestBody{}
  92. xml.Unmarshal(plainText[20:20+length], textRequestBody)
  93. return textRequestBody, nil
  94. }
  95. func Value2CDATA(v string) CDATAText {
  96. //return CDATAText{[]byte("<![CDATA[" + v + "]]>")}
  97. return CDATAText{"<![CDATA[" + v + "]]>"}
  98. }
  99. func MakeEncryptXmlData(fromUserName, toUserName, timestamp, content string) (string, error) {
  100. textResponseBody := &TextResponseBody{}
  101. textResponseBody.FromUserName = Value2CDATA(fromUserName)
  102. textResponseBody.ToUserName = Value2CDATA(toUserName)
  103. textResponseBody.MsgType = Value2CDATA("text")
  104. textResponseBody.Content = Value2CDATA(content)
  105. textResponseBody.CreateTime = timestamp
  106. body, err := xml.MarshalIndent(textResponseBody, " ", " ")
  107. if err != nil {
  108. return "", errors.New("xml marshal error")
  109. }
  110. buf := new(bytes.Buffer)
  111. err = binary.Write(buf, binary.BigEndian, int32(len(body)))
  112. if err != nil {
  113. return "", errors.New("Binary write err:" + err.Error())
  114. }
  115. bodyLength := buf.Bytes()
  116. randomBytes := []byte("abcdefghijklmnop")
  117. plainData := bytes.Join([][]byte{randomBytes, bodyLength, body, []byte(appID)}, nil)
  118. cipherData, err := AesEncrypt(plainData, AesKey)
  119. if err != nil {
  120. return "", errors.New("AesEncrypt error")
  121. }
  122. return base64.StdEncoding.EncodeToString(cipherData), nil
  123. }
  124. // PadLength calculates padding length, from github.com/vgorin/cryptogo
  125. func PadLength(slice_length, blocksize int) (padlen int) {
  126. padlen = blocksize - slice_length%blocksize
  127. if padlen == 0 {
  128. padlen = blocksize
  129. }
  130. return padlen
  131. }
  132. //from github.com/vgorin/cryptogo
  133. func PKCS7Pad(message []byte, blocksize int) (padded []byte) {
  134. // block size must be bigger or equal 2
  135. if blocksize < 1<<1 {
  136. panic("block size is too small (minimum is 2 bytes)")
  137. }
  138. // block size up to 255 requires 1 byte padding
  139. if blocksize < 1<<8 {
  140. // calculate padding length
  141. padlen := PadLength(len(message), blocksize)
  142. // define PKCS7 padding block
  143. padding := bytes.Repeat([]byte{byte(padlen)}, padlen)
  144. // apply padding
  145. padded = append(message, padding...)
  146. return padded
  147. }
  148. // block size bigger or equal 256 is not currently supported
  149. panic("unsupported block size")
  150. }
  151. func AesEncrypt(plainData []byte, aesKey []byte) ([]byte, error) {
  152. k := len(aesKey)
  153. if len(plainData)%k != 0 {
  154. plainData = PKCS7Pad(plainData, k)
  155. }
  156. block, err := aes.NewCipher(aesKey)
  157. if err != nil {
  158. return nil, err
  159. }
  160. iv := make([]byte, aes.BlockSize)
  161. if _, err := io.ReadFull(rand.Reader, iv); err != nil {
  162. return nil, err
  163. }
  164. cipherData := make([]byte, len(plainData))
  165. blockMode := cipher.NewCBCEncrypter(block, iv)
  166. blockMode.CryptBlocks(cipherData, plainData)
  167. return cipherData, nil
  168. }
  169. func MakeEncryptResponseBody(fromUserName, toUserName, content, nonce, timestamp string) ([]byte, error) {
  170. encryptBody := &EncryptResponseBody{}
  171. encryptXmlData, _ := MakeEncryptXmlData(fromUserName, toUserName, timestamp, content)
  172. encryptBody.Encrypt = Value2CDATA(encryptXmlData)
  173. encryptBody.MsgSignature = Value2CDATA(MakeMsgSignature(timestamp, nonce, encryptXmlData))
  174. encryptBody.TimeStamp = timestamp
  175. encryptBody.Nonce = Value2CDATA(nonce)
  176. return xml.MarshalIndent(encryptBody, " ", " ")
  177. }
  178. func SendMsgTypeTextMessage(appid string, ToUserName string, FromUserName string, text string, nonce string, timestamp string, Ctx *context.Context, orgID int64) {
  179. arthorizer, err := GetAuthorizationByAppID(orgID, appid)
  180. if err != nil {
  181. utils.ErrorLog("SendMsgTypeTextMessage error:%s", err)
  182. Ctx.WriteString("success")
  183. return
  184. }
  185. messages, err := GetTextReplyMessagesByKey(arthorizer.UserOrgId, text)
  186. if err != nil {
  187. utils.ErrorLog("SendMsgTypeTextMessage error:%s", err)
  188. Ctx.WriteString("success")
  189. return
  190. }
  191. if len(messages) == 0 {
  192. utils.ErrorLog("SendMsgTypeTextMessage error: messages is nil")
  193. Ctx.WriteString("success")
  194. return
  195. }
  196. r := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
  197. n := r.Intn(len(messages))
  198. for key, item := range messages {
  199. if key == n {
  200. //安全模式下向用户回复消息也需要加密
  201. respBody, e := MakeEncryptResponseBody(FromUserName, ToUserName, item.MessageContent, nonce, timestamp)
  202. if e != nil {
  203. Ctx.WriteString("success")
  204. return
  205. }
  206. Ctx.WriteString(string(respBody))
  207. fmt.Println(string(respBody))
  208. return
  209. }
  210. }
  211. Ctx.WriteString("success")
  212. return
  213. }
  214. //SendSubscribeTextMessage 当用户关注微信公众号,回复ta
  215. func SendSubscribeTextMessage(appid string, ToUserName string, FromUserName string, nonce string, timestamp string, Ctx *context.Context, orgID int64) {
  216. arthorizer, err := GetAuthorizationByAppID(orgID, appid)
  217. if err != nil {
  218. utils.ErrorLog("SendSubscribeTextMessage error:%s", err)
  219. Ctx.WriteString("success")
  220. return
  221. }
  222. message, err := GetOrgSubscribeReplyMessages(arthorizer.UserOrgId)
  223. if err != nil {
  224. utils.ErrorLog("SendSubscribeTextMessage error:%s", err)
  225. Ctx.WriteString("success")
  226. return
  227. }
  228. if message == nil {
  229. utils.ErrorLog("SendSubscribeTextMessage error: message is nil")
  230. Ctx.WriteString("success")
  231. return
  232. }
  233. respBody, e := MakeEncryptResponseBody(FromUserName, ToUserName, message.MessageContent, nonce, timestamp)
  234. if e != nil {
  235. Ctx.WriteString("success")
  236. return
  237. }
  238. Ctx.WriteString(string(respBody))
  239. return
  240. }
  241. func SendClickButtonMessage(appid string, ToUserName string, FromUserName string, keyName string, nonce string, timestamp string, Ctx *context.Context, orgID int64) {
  242. arthorizer, err := GetAuthorizationByAppID(orgID, appid)
  243. if err != nil {
  244. utils.ErrorLog("SendClickButtonMessage error:%s", err)
  245. Ctx.WriteString("success")
  246. return
  247. }
  248. message, err := GetClickButtonReplyMessagesByOrgID(arthorizer.UserOrgId, keyName)
  249. if err != nil {
  250. utils.ErrorLog("SendClickButtonMessage error:%s", err)
  251. Ctx.WriteString("success")
  252. return
  253. }
  254. if message == nil {
  255. utils.ErrorLog("SendClickButtonMessage error: message is nil")
  256. Ctx.WriteString("success")
  257. return
  258. }
  259. respBody, e := MakeEncryptResponseBody(FromUserName, ToUserName, message.MessageContent, nonce, timestamp)
  260. if e != nil {
  261. Ctx.WriteString("success")
  262. return
  263. }
  264. Ctx.WriteString(string(respBody))
  265. return
  266. }
  267. func GetReqPreAuthCode() (code string, err error) {
  268. redisClient := service.RedisClient()
  269. fmt.Println("redisClietent是设么",redisClient)
  270. defer redisClient.Close()
  271. componentAccessToken, err := redisClient.Get("sgj_patient:component_access_token").Result()
  272. fmt.Println("componentAccessToken是设么",componentAccessToken)
  273. if err != nil {
  274. utils.ErrorLog("component_access_token不存在")
  275. return
  276. }
  277. appID := beego.AppConfig.String("openwechatappid")
  278. type reqPreAuthCodeStruct struct {
  279. ComponentAppid string `json:"component_appid"`
  280. }
  281. // 通过 ComponentAccessToken 取 pre_auth_code
  282. // 3、获取预授权码pre_auth_code
  283. var ReqPreAuthCode reqPreAuthCodeStruct
  284. ReqPreAuthCode.ComponentAppid = appID
  285. //创建请求
  286. uri := fmt.Sprintf("%s?component_access_token=%s", "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode", componentAccessToken)
  287. var responseBytes []byte
  288. responseBytes, err = util.PostJSON(uri, ReqPreAuthCode)
  289. if err != nil {
  290. utils.ErrorLog("%s", err)
  291. return
  292. }
  293. var res MPResult
  294. err = json.Unmarshal(responseBytes, &res)
  295. if err != nil {
  296. utils.ErrorLog("%s", err)
  297. return
  298. }
  299. if res.ErrCode > 0 {
  300. utils.ErrorLog("%s", res.ErrMsg)
  301. return
  302. }
  303. var pre_auth_code_struct PreAuthCode
  304. err = json.Unmarshal(responseBytes, &pre_auth_code_struct)
  305. if err != nil {
  306. utils.ErrorLog("pre_auth_code_struct Unmarshal json error:%s", err)
  307. return
  308. }
  309. code = pre_auth_code_struct.PreAuthCode
  310. return
  311. }
  312. //ComponentAPIQueryAuth 使用授权码换取公众号的接口调用凭据和授权信息
  313. func ComponentAPIQueryAuth(AuthorizationCode string, ComponentAccessToken string) ([]byte, error) {
  314. //post的body内容,当前为json格式
  315. reqbody := "{\"component_appid\":\"" + beego.AppConfig.String("openwechatappid") + "\",\"authorization_code\": \"" + AuthorizationCode + "\"}"
  316. //创建请求
  317. postReq, err := http.NewRequest("POST",
  318. "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token="+ComponentAccessToken,
  319. strings.NewReader(reqbody)) //post内容
  320. if err != nil {
  321. utils.ErrorLog("POST请求:omponent/api_query_auth请求创建失败:%s", err)
  322. return nil, err
  323. }
  324. //增加header
  325. postReq.Header.Set("Content-Type", "application/json; encoding=utf-8")
  326. //执行请求
  327. client := &http.Client{}
  328. resp, err := client.Do(postReq)
  329. if err != nil {
  330. utils.ErrorLog("POST请求:创建请求失败:%s", err)
  331. return nil, err
  332. }
  333. //读取响应
  334. body, err := ioutil.ReadAll(resp.Body) //此处可增加输入过滤
  335. if err != nil {
  336. utils.ErrorLog("POST请求:读取body失败:%s", err)
  337. return nil, err
  338. }
  339. resp.Body.Close()
  340. return body, nil
  341. }
  342. //ComponentAPIGetAuthorizerInfo 利用AuthorizerAppid(公众号授权后,获取的appid)拉取公众号信息
  343. func ComponentAPIGetAuthorizerInfo(AuthorizerAppid string, ComponentAccessToken string) ([]byte, error) {
  344. //post的body内容,当前为json格式
  345. reqbody := "{\"component_appid\":\"" + beego.AppConfig.String("openwechatappid") + "\",\"authorizer_appid\": \"" + AuthorizerAppid + "\"}"
  346. //创建请求
  347. postReq, err := http.NewRequest("POST",
  348. "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token="+ComponentAccessToken, //post链接
  349. strings.NewReader(reqbody)) //post内容
  350. if err != nil {
  351. fmt.Println("POST请求:omponent/api_get_authorizer_info请求创建失败")
  352. return nil, err
  353. }
  354. //增加header
  355. postReq.Header.Set("Content-Type", "application/json; encoding=utf-8")
  356. //执行请求
  357. client := &http.Client{}
  358. resp, err := client.Do(postReq)
  359. if err != nil {
  360. fmt.Println("POST请求:创建请求失败")
  361. return nil, err
  362. }
  363. //读取响应
  364. body, err := ioutil.ReadAll(resp.Body) //此处可增加输入过滤
  365. if err != nil {
  366. fmt.Println("POST请求:读取body失败")
  367. return nil, err
  368. }
  369. resp.Body.Close()
  370. return body, nil
  371. }
  372. func PostJSON(uri string, jsonData []byte) ([]byte, error) {
  373. // jsonData, err := json.Marshal(obj)
  374. // if err != nil {
  375. // return nil, err
  376. // }
  377. jsonData = bytes.Replace(jsonData, []byte("\\u003c"), []byte("<"), -1)
  378. jsonData = bytes.Replace(jsonData, []byte("\\u003e"), []byte(">"), -1)
  379. jsonData = bytes.Replace(jsonData, []byte("\\u0026"), []byte("&"), -1)
  380. body := bytes.NewBuffer(jsonData)
  381. response, err := http.Post(uri, "application/json;charset=utf-8", body)
  382. if err != nil {
  383. return nil, err
  384. }
  385. defer response.Body.Close()
  386. if response.StatusCode != http.StatusOK {
  387. return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
  388. }
  389. return ioutil.ReadAll(response.Body)
  390. }
  391. func SendMpWechatMenus(AuthorizerAccessToken string, jsonData []byte) (err error) {
  392. //读取响应
  393. body, err := PostJSON("https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+AuthorizerAccessToken, jsonData)
  394. fmt.Println("公众徐欧文是树森",err)
  395. fmt.Println("body",body)
  396. if err != nil {
  397. return
  398. }
  399. var result MPResult
  400. err = json.Unmarshal([]byte(body), &result)
  401. if err != nil {
  402. return
  403. }
  404. if result.ErrCode > 0 {
  405. utils.ErrorLog("MPResult ERROR:%v", result)
  406. err = errors.New(result.ErrMsg)
  407. return
  408. }
  409. return
  410. }
  411. func DeleteMpWechatMenus(AuthorizerAccessToken string) (err error) {
  412. uri := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s", AuthorizerAccessToken)
  413. b := httplib.Get(uri)
  414. var result MPResult
  415. err = b.ToJSON(&result)
  416. if err != nil {
  417. return
  418. }
  419. if result.ErrCode > 0 {
  420. err = errors.New(result.ErrMsg)
  421. return
  422. }
  423. return
  424. }