wechat.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package svc
  2. import (
  3. "bytes"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "encoding/base64"
  7. "encoding/json"
  8. "fmt"
  9. "image/png"
  10. "io"
  11. "net/url"
  12. "time"
  13. "git.i2edu.net/i2/i2-bill-api/internal/config"
  14. "git.i2edu.net/i2/i2-bill-api/internal/utils"
  15. "git.i2edu.net/i2/i2-bill-api/model"
  16. "github.com/boombuler/barcode"
  17. "github.com/boombuler/barcode/qr"
  18. )
  19. // Wechat defined
  20. type Wechat struct {
  21. RdCli Cache
  22. c config.Config
  23. }
  24. // WechatPhonedecrypt defined TODO
  25. type WechatPhonedecrypt struct {
  26. PhoneNumber string `json:"phoneNumber"`
  27. PurePhoneNumber string `json:"purePhoneNumber"`
  28. CountryCode string `json:"countryCode"`
  29. Watermark struct {
  30. Appid string `json:"appid"`
  31. Timestamp int32 `json:"timestamp"`
  32. } `json:"watermark"`
  33. }
  34. // GetAccessToken defined TODO
  35. func (wc *Wechat) GetAccessToken() (string, error) {
  36. token := ""
  37. err := wc.RdCli.GetCache(cacheWechatTokenPrefix, &token)
  38. if err == model.ErrRdsNotFound || token == "" {
  39. req := utils.Get("https://api.weixin.qq.com/cgi-bin/token")
  40. req.Param("grant_type", "client_credential")
  41. req.Param("secret", wc.c.Weixin.Secret)
  42. req.Param("appid", wc.c.Weixin.Appid)
  43. var res = struct {
  44. AccessToken string `json:"access_token"`
  45. ExpiresIn int `json:"expires_in"`
  46. }{}
  47. err = req.ToJSON(&res)
  48. if err != nil {
  49. return "", err
  50. }
  51. token = res.AccessToken
  52. err := wc.RdCli.SetWithExpire(cacheWechatTokenPrefix, res.AccessToken, time.Duration((res.ExpiresIn-60*2))*time.Second)
  53. if err != nil {
  54. return "", err
  55. }
  56. }
  57. return token, nil
  58. }
  59. // GenQrId defined TODO
  60. func (wc *Wechat) GenQrId(prefix string, params interface{}) (string, error) {
  61. ube, err := json.Marshal(params)
  62. hashCode := utils.HashCode(string(ube))
  63. if err != nil {
  64. return "", err
  65. }
  66. err = wc.RdCli.SetWithExpire(fmt.Sprintf(prefix, hashCode), string(ube), time.Duration(1)*time.Hour)
  67. return fmt.Sprintf("%v", hashCode), err
  68. }
  69. // GenUserQrId defined TODO
  70. func (wc *Wechat) GenUserQrId(prefix string, userId int64, params interface{}) (string, error) {
  71. ube, err := json.Marshal(params)
  72. hashCode := utils.HashCode(string(ube))
  73. if err != nil {
  74. return "", err
  75. }
  76. qrId := fmt.Sprintf("u%v-%v", userId, hashCode)
  77. err = wc.RdCli.SetWithExpire(fmt.Sprintf(prefix, qrId), string(ube), time.Duration(1)*time.Hour)
  78. return qrId, err
  79. }
  80. // GetQrParams defined TODO
  81. func (wc *Wechat) GetQrParams(prefix string, id string, params interface{}) error {
  82. ube := ""
  83. err := wc.RdCli.GetCache(fmt.Sprintf(prefix, id), &ube)
  84. if err != nil {
  85. return err
  86. }
  87. err = json.Unmarshal([]byte(ube), params)
  88. if err != nil {
  89. return err
  90. }
  91. return err
  92. }
  93. // GetQrParams defined TODO
  94. func (wc *Wechat) DecryptMobile(sessionKey, encryptedData, iv string) (string, error) {
  95. key, err := base64.StdEncoding.DecodeString(sessionKey)
  96. if err != nil {
  97. return "", err
  98. }
  99. bIv, err := base64.StdEncoding.DecodeString(iv)
  100. if err != nil {
  101. return "", err
  102. }
  103. bEncryptedData, err := base64.StdEncoding.DecodeString(encryptedData)
  104. if err != nil {
  105. return "", err
  106. }
  107. block, err := aes.NewCipher(key)
  108. if err != nil {
  109. return "", err
  110. }
  111. var decrypt = bEncryptedData
  112. blockMode := cipher.NewCBCDecrypter(block, bIv)
  113. blockMode.CryptBlocks(decrypt, bEncryptedData)
  114. length := len(decrypt)
  115. unpadding := int(decrypt[length-1])
  116. decData := decrypt[:(length - unpadding)]
  117. decryptData := WechatPhonedecrypt{}
  118. err = json.Unmarshal(decData, &decryptData)
  119. if err != nil {
  120. return "", err
  121. }
  122. return decryptData.PhoneNumber, err
  123. }
  124. func (wc *Wechat) GenQrCode(scene, page string) (*model.I2billSysAttachment, error) {
  125. var r io.Reader
  126. var err error
  127. if wc.c.Debug {
  128. r, err = wc.GenTestQrCode(scene, page)
  129. if err != nil {
  130. return nil, err
  131. }
  132. } else {
  133. r1, err := wc.GenGaQrCode(scene, page)
  134. if err != nil {
  135. return nil, err
  136. }
  137. defer func() { _ = r1.Close() }()
  138. r = r1
  139. }
  140. oss, err := GetAliYunOssInstance()
  141. if err != nil {
  142. return nil, err
  143. }
  144. return oss.UploadRead(r, ".png")
  145. }
  146. func (wc *Wechat) GenGaQrCode(scene, page string) (r io.ReadCloser, err error) {
  147. token, err := wc.GetAccessToken()
  148. if err != nil {
  149. return nil, err
  150. }
  151. req := utils.Post("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + token)
  152. _, err = req.JSONBody(map[string]interface{}{"scene": scene, "page": page, "is_hyaline": true})
  153. if err != nil {
  154. return nil, err
  155. }
  156. resp, err := req.Response()
  157. if err != nil {
  158. return nil, err
  159. }
  160. r = resp.Body
  161. return
  162. }
  163. func (wc *Wechat) GenTestQrCode(scene, page string) (r io.Reader, err error) {
  164. ret := "https://open.weixin.qq.com/sns/getexpappinfo" +
  165. "?appid=" + wc.c.Weixin.Appid + "&path=" +
  166. url.QueryEscape(url.QueryEscape(page+".html?scene="+scene+"&is_hyaline=true")) +
  167. "#wechat-redirect"
  168. code, err := qr.Encode(ret, qr.L, qr.Unicode)
  169. if err != nil {
  170. return nil, err
  171. }
  172. code, err = barcode.Scale(code, 256, 256)
  173. if err != nil {
  174. return nil, err
  175. }
  176. buff := &bytes.Buffer{}
  177. err = png.Encode(buff, code)
  178. if err != nil {
  179. return nil, err
  180. }
  181. r = buff
  182. return
  183. }