weapp.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package weapp
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "net/http"
  6. "net/url"
  7. "github.com/sqeven/weapp/util"
  8. )
  9. const (
  10. // BaseURL 微信请求基础URL
  11. BaseURL = "https://api.weixin.qq.com"
  12. codeAPI = "/sns/jscode2session"
  13. )
  14. const (
  15. // WeChatServerError 微信服务器错误时返回返回消息
  16. WeChatServerError = "微信服务器发生错误"
  17. )
  18. // Response 请求微信返回基础数据
  19. type Response struct {
  20. Errcode int `json:"errcode"`
  21. Errmsg string `json:"errmsg"`
  22. }
  23. // PhoneNumber 解密后的用户手机号码信息
  24. type PhoneNumber struct {
  25. PhoneNumber string `json:"phoneNumber"`
  26. PurePhoneNumber string `json:"purePhoneNumber"`
  27. CountryCode string `json:"countryCode"`
  28. Watermark watermark `json:"watermark"`
  29. }
  30. // Userinfo 解密后的用户信息
  31. type Userinfo struct {
  32. OpenID string `json:"openId"`
  33. Nickname string `json:"nickName"`
  34. Gender int `json:"gender"`
  35. Province string `json:"province"`
  36. Language string `json:"language"`
  37. Country string `json:"country"`
  38. City string `json:"city"`
  39. Avatar string `json:"avatarUrl"`
  40. UnionID string `json:"unionId"`
  41. Watermark watermark `json:"watermark"`
  42. }
  43. // LoginResponse 返回给用户的数据
  44. type LoginResponse struct {
  45. OpenID string `json:"openid"`
  46. SessionKey string `json:"session_key"`
  47. // 用户在开放平台的唯一标识符
  48. // 只在满足一定条件的情况下返回
  49. UnionID string `json:"unionid"`
  50. }
  51. type loginResponse struct {
  52. Response
  53. LoginResponse
  54. }
  55. // Login 用户登录
  56. // @appID 小程序 appID
  57. // @secret 小程序的 app secret
  58. // @code 小程序登录时获取的 code
  59. func Login(appID, secret, code string) (lres LoginResponse, err error) {
  60. if code == "" {
  61. err = errors.New("code can not be null")
  62. return
  63. }
  64. api, err := code2url(appID, secret, code)
  65. if err != nil {
  66. return
  67. }
  68. res, err := http.Get(api)
  69. if err != nil {
  70. return
  71. }
  72. defer res.Body.Close()
  73. if res.StatusCode != 200 {
  74. err = errors.New(WeChatServerError)
  75. return
  76. }
  77. var data loginResponse
  78. err = json.NewDecoder(res.Body).Decode(&data)
  79. if err != nil {
  80. return
  81. }
  82. if data.Errcode != 0 {
  83. err = errors.New(data.Errmsg)
  84. return
  85. }
  86. lres = data.LoginResponse
  87. return
  88. }
  89. type watermark struct {
  90. AppID string `json:"appid"`
  91. Timestamp int64 `json:"timestamp"`
  92. }
  93. // DecryptPhoneNumber 解密手机号码
  94. //
  95. // @ssk 通过 Login 向微信服务端请求得到的 session_key
  96. // @data 小程序通过 api 得到的加密数据(encryptedData)
  97. // @iv 小程序通过 api 得到的初始向量(iv)
  98. func DecryptPhoneNumber(ssk, data, iv string) (phone PhoneNumber, err error) {
  99. bts, err := util.CBCDecrypt(ssk, data, iv)
  100. if err != nil {
  101. return
  102. }
  103. err = json.Unmarshal(bts, &phone)
  104. return
  105. }
  106. type group struct {
  107. GID string `json:"openGId"`
  108. }
  109. // DecryptShareInfo 解密转发信息的加密数据
  110. //
  111. // @ssk 通过 Login 向微信服务端请求得到的 session_key
  112. // @data 小程序通过 api 得到的加密数据(encryptedData)
  113. // @iv 小程序通过 api 得到的初始向量(iv)
  114. //
  115. // @gid 小程序唯一群号
  116. func DecryptShareInfo(ssk, data, iv string) (string, error) {
  117. bts, err := util.CBCDecrypt(ssk, data, iv)
  118. if err != nil {
  119. return "", err
  120. }
  121. var g group
  122. err = json.Unmarshal(bts, &g)
  123. return g.GID, err
  124. }
  125. // DecryptUserInfo 解密用户信息
  126. //
  127. // @rawData 不包括敏感信息的原始数据字符串,用于计算签名。
  128. // @encryptedData 包括敏感数据在内的完整用户信息的加密数据
  129. // @signature 使用 sha1( rawData + session_key ) 得到字符串,用于校验用户信息
  130. // @iv 加密算法的初始向量
  131. // @ssk 微信 session_key
  132. func DecryptUserInfo(rawData, encryptedData, signature, iv, ssk string) (ui Userinfo, err error) {
  133. if ok := util.Validate(rawData, ssk, signature); !ok {
  134. err = errors.New("数据校验失败")
  135. return
  136. }
  137. bts, err := util.CBCDecrypt(ssk, encryptedData, iv)
  138. if err != nil {
  139. return
  140. }
  141. err = json.Unmarshal(bts, &ui)
  142. return
  143. }
  144. // 拼接 获取 session_key 的 URL
  145. func code2url(appID, secret, code string) (string, error) {
  146. url, err := url.Parse(BaseURL + codeAPI)
  147. if err != nil {
  148. return "", err
  149. }
  150. query := url.Query()
  151. query.Set("appid", appID)
  152. query.Set("secret", secret)
  153. query.Set("js_code", code)
  154. query.Set("grant_type", "authorization_code")
  155. url.RawQuery = query.Encode()
  156. return url.String(), nil
  157. }