wxofficial.lua 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. require("app.app")
  2. local http = require("socket.http")
  3. local ltn12 = require("ltn12")
  4. local httpclient = require("httpclient")
  5. local M = {}
  6. -- access_token 的 url
  7. M.url_get_access_token = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
  8. -- 获取用户access_token的url
  9. M.url_get_user_access_token = "https://api.weixin.qq.com/sns/oauth2/access_token?grant_type=authorization_code"
  10. -- 获取用户信息的url
  11. M.url_get_user_info = "https://api.weixin.qq.com/sns/userinfo?lang=zh_CN"
  12. -- 授权地址
  13. M.url_auth = "https://open.weixin.qq.com/connect/oauth2/authorize"
  14. -- 模板消息
  15. M.url_template_message = "https://api.weixin.qq.com/cgi-bin/message/template/send"
  16. -- 获取 access_token
  17. M.get_access_token = function(appid, appsecret,rds)
  18. -- local value = cache.get_json("wxofficial_info")
  19. -- if value then
  20. -- -- 是否距离过期至少还有5分钟
  21. -- if value.expires_seconds > os.time() + 300 then
  22. -- return true, value.access_token
  23. -- end
  24. -- end
  25. -- 如果 appid 和 appsecret 为空,则返回 nil
  26. if appid == nil or appid == "" then
  27. return false, "appid 为空"
  28. end
  29. if appsecret == nil or appsecret == "" then
  30. return false, "appsecret 为空"
  31. end
  32. local cache_data = rds:get("wxofficial_info_"..appid)
  33. if cache_data then
  34. -- print("cache_data:",cache_data)
  35. cache_data = cjson.decode(cache_data)
  36. if cache_data.update_time and cache_data.access_token then
  37. -- 检查是否过期
  38. if os.time() - cache_data.update_time < 7200 then
  39. return true,cache_data.access_token
  40. end
  41. end
  42. end
  43. local result, data = httpclient.get(M.url_get_access_token .. "&appid=" .. appid .. "&secret=" .. appsecret)
  44. if not result then
  45. return false, "get_access_token failed:"..data
  46. end
  47. local body = cjson.decode(data)
  48. if body.errcode ~= nil and body.errcode ~= 0 then
  49. return false, "get_access_token failed:"..body.errmsg
  50. end
  51. -- 更新
  52. local ok = rds:setex("wxofficial_info_"..appid,7200,cjson.encode({
  53. update_time = os.time(),
  54. access_token = body.access_token
  55. }))
  56. return true,body.access_token
  57. end
  58. -- 获取用户access_token
  59. M.get_user_access_token = function(appid, appsecret, code)
  60. local result, data = httpclient.get(M.url_get_user_access_token .. "&appid=" .. appid .. "&secret=" .. appsecret .. "&code=" .. code)
  61. if not result then
  62. return false, data
  63. end
  64. local body = cjson.decode(data)
  65. if body.errcode ~= nil and body.errcode ~= 0 then
  66. return false, body.errmsg
  67. end
  68. return true, body
  69. end
  70. -- 获取用户信息
  71. M.get_user_info = function(access_token, openid)
  72. local url = M.url_get_user_info .. "&access_token=" .. access_token .. "&openid=" .. openid
  73. local result, data = httpclient.get(url)
  74. if not result then
  75. return false, data
  76. end
  77. local body = cjson.decode(data)
  78. if body.errcode ~= nil and body.errcode ~= 0 then
  79. return false, body.errmsg
  80. end
  81. return true, body
  82. end
  83. -- 生成授权地址
  84. M.generate_auth_url = function(appid, redirect_uri)
  85. return M.url_auth .. "?appid=" .. appid .. "&redirect_uri=" .. redirect_uri .. "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect"
  86. end
  87. -- 发送模板消息
  88. M.send_template_message = function(appid, appsecret, openid, template_id, jump_url, data,rds)
  89. local ok, access_token = M.get_access_token(appid, appsecret,rds)
  90. if not ok then
  91. return false, access_token
  92. end
  93. local url = M.url_template_message .. "?access_token=" .. access_token
  94. local body = {
  95. touser = openid,
  96. template_id = template_id,
  97. url = jump_url,
  98. data = data,
  99. appid = appid
  100. }
  101. local result, response = httpclient.post(url, {
  102. ["Content-Type"] = "application/json"
  103. }, cjson.encode(body))
  104. if not result then
  105. return false, response
  106. end
  107. local res_json = cjson.decode(response)
  108. if res_json.errcode ~= 0 then
  109. return false, res_json.errmsg
  110. end
  111. return true
  112. end
  113. -- 生成一次性订阅确认地址
  114. M.generate_subscribe_confirm_url = function(appid, scene, template_id, redirect_uri, reserved)
  115. return "https://mp.weixin.qq.com/mp/subscribemsg?action=get_confirm&appid=" .. appid .. "&scene=" .. scene .. "&template_id=" .. template_id .. "&redirect_url=" .. redirect_uri .. "&#wechat_redirect"
  116. end
  117. --------------------------------------------------------------------------------
  118. -- 以下为新增的部分,用于微信JS SDK 配置
  119. -- 生成随机字符串函数
  120. local function generate_nonce_str(length)
  121. --math.randomseed(os.time() + math.random()) -- 增加随机种子
  122. local chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
  123. local res = {}
  124. for i = 1, (length or 16) do
  125. local rand = math.random(1, #chars)
  126. res[#res+1] = string.sub(chars, rand, rand)
  127. end
  128. return table.concat(res)
  129. end
  130. -- 获取 jsapi_ticket
  131. M.get_jsapi_ticket = function(appid, appsecret)
  132. local ticket_info = cache.get_json("wxjsapi_ticket")
  133. if ticket_info then
  134. if ticket_info.expires > os.time() + 300 then
  135. return true, ticket_info.ticket
  136. end
  137. end
  138. local ok, access_token = M.get_access_token(appid, appsecret)
  139. if not ok then
  140. return false, access_token
  141. end
  142. local ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" .. access_token .. "&type=jsapi"
  143. local result, data = httpclient.get(ticket_url)
  144. if not result then
  145. return false, data
  146. end
  147. local body = cjson.decode(data)
  148. if body.errcode ~= 0 then
  149. return false, body.errmsg
  150. end
  151. cache.set_json("wxjsapi_ticket", {
  152. ticket = body.ticket,
  153. expires = os.time() + body.expires_in
  154. })
  155. return true, body.ticket
  156. end
  157. -- 获取微信JS SDK所需的配置参数
  158. -- 参数 current_url 必须是当前网页的完整 URL(不包含#号后的部分)
  159. M.get_js_sdk_config = function(appid, appsecret, current_url)
  160. local ok, jsapi_ticket = M.get_jsapi_ticket(appid, appsecret)
  161. if not ok then
  162. return false, jsapi_ticket..",err"
  163. end
  164. local nonceStr = generate_nonce_str(16)
  165. local timestamp = os.time()
  166. -- 拼接待签名字符串,注意顺序及格式严格按照微信要求
  167. local str = "jsapi_ticket=" .. jsapi_ticket .. "&noncestr=" .. nonceStr .. "&timestamp=" .. timestamp .. "&url=" .. current_url
  168. -- 计算 SHA1 签名(需在 OpenResty 下使用 ngx.sha1_bin 和 resty.string.to_hex)
  169. local signature = codec.sha1(str)
  170. return true, {
  171. appId = appid,
  172. timestamp = timestamp,
  173. nonceStr = nonceStr,
  174. signature = signature
  175. }
  176. end
  177. --------------------------------------------------------------------------------
  178. return M