weixin_authorize_superayi 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +11 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +72 -0
- data/Rakefile +1 -0
- data/lib/weixin_authorize/api/custom.rb +176 -0
- data/lib/weixin_authorize/api/data_cube.rb +8 -0
- data/lib/weixin_authorize/api/groups.rb +60 -0
- data/lib/weixin_authorize/api/mass.rb +80 -0
- data/lib/weixin_authorize/api/media.rb +149 -0
- data/lib/weixin_authorize/api/menu.rb +36 -0
- data/lib/weixin_authorize/api/oauth.rb +50 -0
- data/lib/weixin_authorize/api/qrcode.rb +53 -0
- data/lib/weixin_authorize/api/template.rb +43 -0
- data/lib/weixin_authorize/api/user.rb +69 -0
- data/lib/weixin_authorize/api.rb +3 -0
- data/lib/weixin_authorize/carrierwave/weixin_uploader.rb +4 -0
- data/lib/weixin_authorize/client.rb +95 -0
- data/lib/weixin_authorize/config.rb +35 -0
- data/lib/weixin_authorize/handler/exceptions.rb +5 -0
- data/lib/weixin_authorize/handler/global_code.rb +127 -0
- data/lib/weixin_authorize/handler/result_handler.rb +52 -0
- data/lib/weixin_authorize/handler.rb +3 -0
- data/lib/weixin_authorize/js_ticket/object_store.rb +21 -0
- data/lib/weixin_authorize/js_ticket/redis_store.rb +41 -0
- data/lib/weixin_authorize/js_ticket/store.rb +40 -0
- data/lib/weixin_authorize/token/object_store.rb +25 -0
- data/lib/weixin_authorize/token/redis_store.rb +38 -0
- data/lib/weixin_authorize/token/store.rb +72 -0
- data/lib/weixin_authorize/version.rb +3 -0
- data/lib/weixin_authorize.rb +97 -0
- data/spec/1_fetch_access_token_spec.rb +43 -0
- data/spec/2_fetch_jsticket_spec.rb +10 -0
- data/spec/api/custom_spec.rb +71 -0
- data/spec/api/groups_spec.rb +74 -0
- data/spec/api/mass_spec.rb +70 -0
- data/spec/api/media_spec.rb +82 -0
- data/spec/api/medias/favicon.ico +0 -0
- data/spec/api/medias/ruby-logo.jpg +0 -0
- data/spec/api/menu_spec.rb +26 -0
- data/spec/api/qrcode_spec.rb +22 -0
- data/spec/api/template_spec.rb +40 -0
- data/spec/api/user_spec.rb +26 -0
- data/spec/spec_helper.rb +130 -0
- data/weixin_authorize_superayi.gemspec +48 -0
- metadata +301 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module WeixinAuthorize
|
3
|
+
module Api
|
4
|
+
module Oauth
|
5
|
+
|
6
|
+
# 网站应用微信登录授权URL
|
7
|
+
# 文档:http://t.cn/RyZVWEY
|
8
|
+
def qrcode_authorize_url(redirect_uri, scope="snsapi_login", state="web_wx_login")
|
9
|
+
uri = encode_url(redirect_uri)
|
10
|
+
WeixinAuthorize.open_endpoint("/connect/qrconnect?appid=#{app_id}&redirect_uri=#{uri}&response_type=code&scope=#{scope}&state=#{state}#wechat_redirect")
|
11
|
+
end
|
12
|
+
|
13
|
+
# 应用授权作用域: scope
|
14
|
+
# snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),
|
15
|
+
# snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
|
16
|
+
# default is snsapi_base
|
17
|
+
# state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值
|
18
|
+
|
19
|
+
# 如果用户点击同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE
|
20
|
+
def authorize_url(redirect_uri, scope="snsapi_base", state="weixin")
|
21
|
+
uri = encode_url(redirect_uri)
|
22
|
+
WeixinAuthorize.open_endpoint("/connect/oauth2/authorize?appid=#{app_id}&redirect_uri=#{uri}&response_type=code&scope=#{scope}&state=#{state}#wechat_redirect")
|
23
|
+
end
|
24
|
+
|
25
|
+
# 首先请注意,这里通过code换取的网页授权access_token,与基础支持中的access_token不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。
|
26
|
+
|
27
|
+
# 微信通过请求 #authorize_url 方法后,会返回一个code到redirect_uri中
|
28
|
+
def get_oauth_access_token(code)
|
29
|
+
WeixinAuthorize.http_get_without_token("/sns/oauth2/access_token?appid=#{app_id}&secret=#{app_secret}&code=#{code}&grant_type=authorization_code", {}, "api")
|
30
|
+
end
|
31
|
+
|
32
|
+
# refresh_token: 填写通过access_token获取到的refresh_token参数
|
33
|
+
def refresh_oauth2_token(refresh_token)
|
34
|
+
WeixinAuthorize.http_get_without_token("/sns/oauth2/refresh_token?appid=#{app_id}&grant_type=refresh_token&refresh_token=#{refresh_token}", {}, "api")
|
35
|
+
end
|
36
|
+
|
37
|
+
# 如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。
|
38
|
+
def get_oauth_userinfo(openid, oauth_token, lang="zh_CN")
|
39
|
+
WeixinAuthorize.http_get_without_token("/sns/userinfo?access_token=#{oauth_token}&openid=#{openid}&lang=#{lang}", {}, "api")
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def encode_url(uri)
|
45
|
+
ERB::Util.url_encode(uri)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module WeixinAuthorize
|
3
|
+
module Api
|
4
|
+
module Qrcode
|
5
|
+
# http://mp.weixin.qq.com/wiki/index.php?title=生成带参数的二维码
|
6
|
+
|
7
|
+
# 临时二维码
|
8
|
+
def create_qr_scene(scene_id, expire_seconds=1800)
|
9
|
+
qrcode_infos = {action_name: "QR_SCENE", expire_seconds: expire_seconds}
|
10
|
+
qrcode_infos.merge!(action_info(scene_id))
|
11
|
+
http_post(qrcode_base_url, qrcode_infos)
|
12
|
+
end
|
13
|
+
|
14
|
+
# 永久二维码
|
15
|
+
# options: scene_id, scene_str
|
16
|
+
def create_qr_limit_scene(options)
|
17
|
+
scene_id = options[:scene_id]
|
18
|
+
qrcode_infos = {action_name: "QR_LIMIT_SCENE"}
|
19
|
+
qrcode_infos.merge!(action_info(scene_id))
|
20
|
+
http_post(qrcode_base_url, qrcode_infos)
|
21
|
+
end
|
22
|
+
|
23
|
+
# 为永久的字符串参数值
|
24
|
+
# options: scene_str
|
25
|
+
def create_qr_limit_str_scene(options)
|
26
|
+
scene_str = options[:scene_str]
|
27
|
+
qrcode_infos = {action_name: "QR_LIMIT_STR_SCENE"}
|
28
|
+
qrcode_infos.merge!(action_info(nil, scene_str))
|
29
|
+
http_post(qrcode_base_url, qrcode_infos)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# 通过ticket换取二维码, 直接访问即可显示!
|
34
|
+
def qr_code_url(ticket)
|
35
|
+
WeixinAuthorize.mp_endpoint("/showqrcode?ticket=#{ticket}")
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def qrcode_base_url
|
41
|
+
"/qrcode/create"
|
42
|
+
end
|
43
|
+
|
44
|
+
def action_info(scene_id, scene_str=nil)
|
45
|
+
scene_info = {}
|
46
|
+
scene_info[:scene_id] = scene_id if !scene_id.nil?
|
47
|
+
scene_info[:scene_str] = scene_str if !scene_str.nil?
|
48
|
+
{action_info: {scene: scene_info}}
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module WeixinAuthorize
|
3
|
+
module Api
|
4
|
+
module Template
|
5
|
+
|
6
|
+
# 设置所属行业
|
7
|
+
# 需要选择公众账号服务所处的2个行业,每月可更改1次所选行业;
|
8
|
+
# 初始化行业时,传入两个,每月更改时,传入一个即可。
|
9
|
+
def set_template_industry(industry_id1, industry_id2="")
|
10
|
+
industries = {industry_id1: industry_id1}
|
11
|
+
if industry_id2 != ""
|
12
|
+
industries.merge!({industry_id2: industry_id2})
|
13
|
+
end
|
14
|
+
http_post("/template/api_set_industry", industries)
|
15
|
+
end
|
16
|
+
|
17
|
+
# 获得模板ID
|
18
|
+
# code: 模板库中模板的编号,有“TM**”和“OPENTMTM**”等形式
|
19
|
+
def add_template(code)
|
20
|
+
http_post("/template/api_add_template", template_id_short: code)
|
21
|
+
end
|
22
|
+
|
23
|
+
# 发送模板消息
|
24
|
+
def send_template_msg(touser, template_id, url, topcolor, data)
|
25
|
+
msg = {
|
26
|
+
touser: touser, template_id: template_id,
|
27
|
+
url: url, topcolor: topcolor, data: data
|
28
|
+
}
|
29
|
+
http_post("/message/template/send", msg)
|
30
|
+
end
|
31
|
+
|
32
|
+
# 发送模板消息(小程序)
|
33
|
+
def send_miniprogram_template_msg(touser, template_id, url, miniprogram, data)
|
34
|
+
msg = {
|
35
|
+
touser: touser, template_id: template_id,
|
36
|
+
url: url, miniprogram: miniprogram, data: data
|
37
|
+
}
|
38
|
+
http_post("/message/template/send", msg)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module WeixinAuthorize
|
3
|
+
module Api
|
4
|
+
module User
|
5
|
+
|
6
|
+
# 获取用户基本信息
|
7
|
+
# https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
|
8
|
+
# lang: zh_CN, zh_TW, en
|
9
|
+
def user(openid, lang="zh_CN")
|
10
|
+
user_info_url = "#{user_base_url}/info"
|
11
|
+
http_get(user_info_url, {openid: openid, lang: lang})
|
12
|
+
end
|
13
|
+
|
14
|
+
# 批量获取用户基本信息
|
15
|
+
# https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN
|
16
|
+
# POST数据格式:JSON
|
17
|
+
# POST数据例子:
|
18
|
+
# {
|
19
|
+
# "user_list": [
|
20
|
+
# {
|
21
|
+
# "openid": "otvxTs4dckWG7imySrJd6jSi0CWE",
|
22
|
+
# "lang": "zh-CN"
|
23
|
+
# },
|
24
|
+
# {
|
25
|
+
# "openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg",
|
26
|
+
# "lang": "zh-CN"
|
27
|
+
# }
|
28
|
+
# ]
|
29
|
+
# }
|
30
|
+
def users(user_list)
|
31
|
+
user_info_batchget_url = "#{user_base_url}/info/batchget"
|
32
|
+
post_body = user_list
|
33
|
+
http_post(user_info_batchget_url, post_body)
|
34
|
+
end
|
35
|
+
|
36
|
+
# 获取关注者列表
|
37
|
+
# https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
|
38
|
+
def followers(next_openid="")
|
39
|
+
followers_url = "#{user_base_url}/get"
|
40
|
+
http_get(followers_url, {next_openid: next_openid})
|
41
|
+
end
|
42
|
+
|
43
|
+
# 设置备注名
|
44
|
+
# http请求方式: POST(请使用https协议)
|
45
|
+
# https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=ACCESS_TOKEN
|
46
|
+
# POST数据格式:JSON
|
47
|
+
# POST数据例子:
|
48
|
+
# {
|
49
|
+
# "openid":"oDF3iY9ffA-hqb2vVvbr7qxf6A0Q",
|
50
|
+
# "remark":"pangzi"
|
51
|
+
# }
|
52
|
+
def update_remark(openid, remark)
|
53
|
+
update_url = "/user/info/updateremark"
|
54
|
+
post_body = {
|
55
|
+
openid: openid,
|
56
|
+
remark: remark
|
57
|
+
}
|
58
|
+
http_post(update_url, post_body)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def user_base_url
|
64
|
+
"/user"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "monitor"
|
3
|
+
require "redis"
|
4
|
+
require 'digest/md5'
|
5
|
+
module WeixinAuthorize
|
6
|
+
|
7
|
+
class Client
|
8
|
+
|
9
|
+
include MonitorMixin
|
10
|
+
|
11
|
+
include Api::User
|
12
|
+
include Api::Menu
|
13
|
+
include Api::Custom
|
14
|
+
include Api::Groups
|
15
|
+
include Api::Qrcode
|
16
|
+
include Api::Media
|
17
|
+
include Api::Mass
|
18
|
+
include Api::Oauth
|
19
|
+
include Api::Template
|
20
|
+
|
21
|
+
attr_accessor :app_id, :app_secret, :expired_at # Time.now + expires_in
|
22
|
+
attr_accessor :access_token, :redis_key, :custom_access_token
|
23
|
+
attr_accessor :jsticket, :jsticket_expired_at, :jsticket_redis_key
|
24
|
+
|
25
|
+
# options: redis_key, custom_access_token
|
26
|
+
def initialize(app_id, app_secret, options={})
|
27
|
+
@app_id = app_id
|
28
|
+
@app_secret = app_secret
|
29
|
+
@jsticket_expired_at = @expired_at = Time.now.to_i
|
30
|
+
@redis_key = security_redis_key(options[:redis_key] || "weixin_#{app_id}")
|
31
|
+
@jsticket_redis_key = security_redis_key("js_sdk_#{app_id}")
|
32
|
+
@custom_access_token = options[:custom_access_token]
|
33
|
+
super() # Monitor#initialize
|
34
|
+
end
|
35
|
+
|
36
|
+
# return token
|
37
|
+
def get_access_token
|
38
|
+
return custom_access_token if !custom_access_token.nil?
|
39
|
+
synchronize{ token_store.access_token }
|
40
|
+
end
|
41
|
+
|
42
|
+
# 检查appid和app_secret是否有效。
|
43
|
+
def is_valid?
|
44
|
+
return true if !custom_access_token.nil?
|
45
|
+
token_store.valid?
|
46
|
+
end
|
47
|
+
|
48
|
+
def token_store
|
49
|
+
Token::Store.init_with(self)
|
50
|
+
end
|
51
|
+
|
52
|
+
def jsticket_store
|
53
|
+
JsTicket::Store.init_with(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_jsticket
|
57
|
+
jsticket_store.jsticket
|
58
|
+
end
|
59
|
+
|
60
|
+
# 获取js sdk 签名包
|
61
|
+
def get_jssign_package(url)
|
62
|
+
timestamp = Time.now.to_i
|
63
|
+
noncestr = SecureRandom.hex(16)
|
64
|
+
str = "jsapi_ticket=#{get_jsticket}&noncestr=#{noncestr}×tamp=#{timestamp}&url=#{url}";
|
65
|
+
signature = Digest::SHA1.hexdigest(str)
|
66
|
+
{
|
67
|
+
"appId" => app_id, "nonceStr" => noncestr,
|
68
|
+
"timestamp" => timestamp, "url" => url,
|
69
|
+
"signature" => signature, "rawString" => str
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
# 暴露出:http_get,http_post两个方法,方便第三方开发者扩展未开发的微信API。
|
74
|
+
def http_get(url, url_params={}, endpoint="plain")
|
75
|
+
url_params = url_params.merge(access_token_param)
|
76
|
+
WeixinAuthorize.http_get_without_token(url, url_params, endpoint)
|
77
|
+
end
|
78
|
+
|
79
|
+
def http_post(url, post_body={}, url_params={}, endpoint="plain")
|
80
|
+
url_params = access_token_param.merge(url_params)
|
81
|
+
WeixinAuthorize.http_post_without_token(url, post_body, url_params, endpoint)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def access_token_param
|
87
|
+
{access_token: get_access_token}
|
88
|
+
end
|
89
|
+
|
90
|
+
def security_redis_key(key)
|
91
|
+
Digest::MD5.hexdigest(key.to_s).upcase
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module WeixinAuthorize
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
attr_accessor :config
|
6
|
+
|
7
|
+
def configure
|
8
|
+
yield self.config ||= Config.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def weixin_redis
|
12
|
+
return nil if config.nil?
|
13
|
+
@redis ||= config.redis
|
14
|
+
end
|
15
|
+
|
16
|
+
def key_expired
|
17
|
+
config.key_expired rescue 100
|
18
|
+
end
|
19
|
+
|
20
|
+
# 可选配置: RestClient timeout, etc.
|
21
|
+
# key 必须是符号
|
22
|
+
# 如果出现 RestClient::SSLCertificateNotVerified Exception: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
|
23
|
+
# 这个错,除了改 verify_ssl: true,请参考:http://www.extendi.it/blog/2015/5/23/47-sslv3-read-server-certificate-b-certificate-verify-failed
|
24
|
+
def rest_client_options
|
25
|
+
if config.nil?
|
26
|
+
return {timeout: 5, open_timeout: 5, verify_ssl: true}
|
27
|
+
end
|
28
|
+
config.rest_client_options
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Config
|
33
|
+
attr_accessor :redis, :rest_client_options, :key_expired
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module WeixinAuthorize
|
3
|
+
|
4
|
+
GLOBAL_CODES = {
|
5
|
+
-1 => "系统繁忙",
|
6
|
+
0 => "请求成功",
|
7
|
+
40001 => "获取access_token时AppSecret错误,或者access_token无效",
|
8
|
+
40002 => "不合法的凭证类型",
|
9
|
+
40003 => "不合法的OpenID",
|
10
|
+
40004 => "不合法的媒体文件类型",
|
11
|
+
40005 => "不合法的文件类型",
|
12
|
+
40006 => "不合法的文件大小",
|
13
|
+
40007 => "不合法的媒体文件id",
|
14
|
+
40008 => "不合法的消息类型",
|
15
|
+
40009 => "不合法的图片文件大小",
|
16
|
+
40010 => "不合法的语音文件大小",
|
17
|
+
40011 => "不合法的视频文件大小",
|
18
|
+
40012 => "不合法的缩略图文件大小",
|
19
|
+
40013 => "不合法的APPID",
|
20
|
+
40014 => "不合法的access_token",
|
21
|
+
40015 => "不合法的菜单类型",
|
22
|
+
40016 => "不合法的按钮个数",
|
23
|
+
40017 => "不合法的按钮个数",
|
24
|
+
40018 => "不合法的按钮名字长度",
|
25
|
+
40019 => "不合法的按钮KEY长度",
|
26
|
+
40020 => "不合法的按钮URL长度",
|
27
|
+
40021 => "不合法的菜单版本号",
|
28
|
+
40022 => "不合法的子菜单级数",
|
29
|
+
40023 => "不合法的子菜单按钮个数",
|
30
|
+
40024 => "不合法的子菜单按钮类型",
|
31
|
+
40025 => "不合法的子菜单按钮名字长度",
|
32
|
+
40026 => "不合法的子菜单按钮KEY长度",
|
33
|
+
40027 => "不合法的子菜单按钮URL长度",
|
34
|
+
40028 => "不合法的自定义菜单使用用户",
|
35
|
+
40029 => "不合法的oauth_code",
|
36
|
+
40030 => "不合法的refresh_token",
|
37
|
+
40031 => "不合法的openid列表",
|
38
|
+
40032 => "不合法的openid列表长度",
|
39
|
+
40033 => "不合法的请求字符,不能包含xxxx格式的字符",
|
40
|
+
40035 => "不合法的参数",
|
41
|
+
40038 => "不合法的请求格式",
|
42
|
+
40039 => "不合法的URL长度",
|
43
|
+
40050 => "不合法的分组id",
|
44
|
+
40051 => "分组名字不合法",
|
45
|
+
41001 => "缺少access_token参数",
|
46
|
+
41002 => "缺少appid参数",
|
47
|
+
41003 => "缺少refresh_token参数",
|
48
|
+
41004 => "缺少secret参数",
|
49
|
+
41005 => "缺少多媒体文件数据",
|
50
|
+
41006 => "缺少media_id参数",
|
51
|
+
41007 => "缺少子菜单数据",
|
52
|
+
41008 => "缺少oauth code",
|
53
|
+
41009 => "缺少openid",
|
54
|
+
42001 => "access_token超时",
|
55
|
+
42002 => "refresh_token超时",
|
56
|
+
42003 => "oauth_code超时",
|
57
|
+
43001 => "需要GET请求",
|
58
|
+
43002 => "需要POST请求",
|
59
|
+
43003 => "需要HTTPS请求",
|
60
|
+
43004 => "需要接收者关注",
|
61
|
+
43005 => "需要好友关系",
|
62
|
+
44001 => "多媒体文件为空",
|
63
|
+
44002 => "POST的数据包为空",
|
64
|
+
44003 => "图文消息内容为空",
|
65
|
+
44004 => "文本消息内容为空",
|
66
|
+
45001 => "多媒体文件大小超过限制",
|
67
|
+
45002 => "消息内容超过限制",
|
68
|
+
45003 => "标题字段超过限制",
|
69
|
+
45004 => "描述字段超过限制",
|
70
|
+
45005 => "链接字段超过限制",
|
71
|
+
45006 => "图片链接字段超过限制",
|
72
|
+
45007 => "语音播放时间超过限制",
|
73
|
+
45008 => "图文消息超过限制",
|
74
|
+
45009 => "接口调用超过限制",
|
75
|
+
45010 => "创建菜单个数超过限制",
|
76
|
+
45015 => "回复时间超过限制",
|
77
|
+
45016 => "系统分组,不允许修改",
|
78
|
+
45017 => "分组名字过长",
|
79
|
+
45018 => "分组数量超过上限",
|
80
|
+
46001 => "不存在媒体数据",
|
81
|
+
46002 => "不存在的菜单版本",
|
82
|
+
46003 => "不存在的菜单数据",
|
83
|
+
46004 => "不存在的用户",
|
84
|
+
47001 => "解析JSON/XML内容错误",
|
85
|
+
48001 => "api功能未授权",
|
86
|
+
50001 => "用户未授权该api",
|
87
|
+
50002 => "用户受限,可能是违规后接口被封禁",
|
88
|
+
61451 => "参数错误(invalid parameter)",
|
89
|
+
61452 => "无效客服账号(invalid kf_account)",
|
90
|
+
61453 => "客服帐号已存在(kf_account exsited)",
|
91
|
+
61454 => "客服帐号名长度超过限制(仅允许10个英文字符,不包括@及@后的公众号的微信号)(invalid kf_acount length)",
|
92
|
+
61455 => "客服帐号名包含非法字符(仅允许英文+数字)(illegal character in kf_account)",
|
93
|
+
61456 => "客服帐号个数超过限制(10个客服账号)(kf_account count exceeded)",
|
94
|
+
61457 => "无效头像文件类型(invalid file type)",
|
95
|
+
61450 => "系统错误(system error)",
|
96
|
+
61500 => "日期格式错误",
|
97
|
+
61501 => "日期范围错误",
|
98
|
+
9001001 => "POST数据参数不合法",
|
99
|
+
9001002 => "远端服务不可用",
|
100
|
+
9001003 => "Ticket不合法",
|
101
|
+
9001004 => "获取摇周边用户信息失败",
|
102
|
+
9001005 => "获取商户信息失败",
|
103
|
+
9001006 => "获取OpenID失败",
|
104
|
+
9001007 => "上传文件缺失",
|
105
|
+
9001008 => "上传素材的文件类型不合法",
|
106
|
+
9001009 => "上传素材的文件尺寸不合法",
|
107
|
+
9001010 => "上传失败",
|
108
|
+
9001020 => "帐号不合法",
|
109
|
+
9001021 => "已有设备激活率低于50%,不能新增设备",
|
110
|
+
9001022 => "设备申请数不合法,必须为大于0的数字",
|
111
|
+
9001023 => "已存在审核中的设备ID申请",
|
112
|
+
9001024 => "一次查询设备ID数量不能超过50",
|
113
|
+
9001025 => "设备ID不合法",
|
114
|
+
9001026 => "页面ID不合法",
|
115
|
+
9001027 => "页面参数不合法",
|
116
|
+
9001028 => "一次删除页面ID数量不能超过10",
|
117
|
+
9001029 => "页面已应用在设备中,请先解除应用关系再删除",
|
118
|
+
9001030 => "一次查询页面ID数量不能超过50",
|
119
|
+
9001031 => "时间区间不合法",
|
120
|
+
9001032 => "保存设备与页面的绑定关系参数错误",
|
121
|
+
9001033 => "门店ID不合法",
|
122
|
+
9001034 => "设备备注信息过长",
|
123
|
+
9001035 => "设备申请参数不合法",
|
124
|
+
9001036 => "查询起始值begin不合法"
|
125
|
+
}unless defined?(GLOBAL_CODES)
|
126
|
+
|
127
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module WeixinAuthorize
|
3
|
+
|
4
|
+
class ResultHandler
|
5
|
+
|
6
|
+
attr_accessor :code, :cn_msg, :en_msg, :result
|
7
|
+
|
8
|
+
def initialize(code, en_msg, result={})
|
9
|
+
@code = code || OK_CODE
|
10
|
+
@en_msg = en_msg || OK_MSG
|
11
|
+
@cn_msg = GLOBAL_CODES[@code.to_i]
|
12
|
+
@result = package_result(result)
|
13
|
+
end
|
14
|
+
|
15
|
+
# This method is to valid the current request if is true or is false
|
16
|
+
def is_ok?
|
17
|
+
code == OK_CODE
|
18
|
+
end
|
19
|
+
alias_method :ok?, :is_ok?
|
20
|
+
|
21
|
+
# e.g.:
|
22
|
+
# 45009: api freq out of limit(接口调用超过限制)
|
23
|
+
def full_message
|
24
|
+
"#{code}: #{en_msg}(#{cn_msg})."
|
25
|
+
end
|
26
|
+
alias_method :full_messages, :full_message
|
27
|
+
|
28
|
+
def full_error_message
|
29
|
+
full_message if !is_ok?
|
30
|
+
end
|
31
|
+
alias_method :full_error_messages, :full_error_message
|
32
|
+
alias_method :errors, :full_error_message
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# if define Rails constant
|
37
|
+
# result = WeixinAuthorize::ResultHandler.new("0", "success", {:ok => "true"})
|
38
|
+
# result.result["ok"] #=> true
|
39
|
+
# result.result[:ok] #=> true
|
40
|
+
# result.result['ok'] #=> true
|
41
|
+
def package_result(result)
|
42
|
+
return result if !result.is_a?(Hash)
|
43
|
+
if defined?(Rails)
|
44
|
+
ActiveSupport::HashWithIndifferentAccess.new(result)
|
45
|
+
else
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module WeixinAuthorize
|
2
|
+
module JsTicket
|
3
|
+
class ObjectStore < Store
|
4
|
+
|
5
|
+
def jsticket_expired?
|
6
|
+
# 如果当前token过期时间小于现在的时间,则重新获取一次
|
7
|
+
client.jsticket_expired_at <= Time.now.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def jsticket
|
11
|
+
super
|
12
|
+
client.jsticket
|
13
|
+
end
|
14
|
+
|
15
|
+
def refresh_jsticket
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module WeixinAuthorize
|
2
|
+
module JsTicket
|
3
|
+
class RedisStore < Store
|
4
|
+
JSTICKET = "jsticket"
|
5
|
+
EXPIRED_AT = "expired_at"
|
6
|
+
|
7
|
+
def jsticket_expired?
|
8
|
+
weixin_redis.hvals(client.jsticket_redis_key).empty?
|
9
|
+
end
|
10
|
+
|
11
|
+
def refresh_jsticket
|
12
|
+
super
|
13
|
+
weixin_redis.hmset(
|
14
|
+
client.jsticket_redis_key,
|
15
|
+
JSTICKET,
|
16
|
+
client.jsticket,
|
17
|
+
EXPIRED_AT,
|
18
|
+
client.jsticket_expired_at
|
19
|
+
)
|
20
|
+
weixin_redis.expireat(
|
21
|
+
client.jsticket_redis_key,
|
22
|
+
client.jsticket_expired_at.to_i
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def jsticket
|
27
|
+
super
|
28
|
+
client.jsticket = weixin_redis.hget(client.jsticket_redis_key, JSTICKET)
|
29
|
+
client.jsticket_expired_at = weixin_redis.hget(
|
30
|
+
client.jsticket_redis_key,
|
31
|
+
EXPIRED_AT
|
32
|
+
)
|
33
|
+
client.jsticket
|
34
|
+
end
|
35
|
+
|
36
|
+
def weixin_redis
|
37
|
+
WeixinAuthorize.weixin_redis
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module WeixinAuthorize
|
3
|
+
module JsTicket
|
4
|
+
class Store
|
5
|
+
|
6
|
+
attr_accessor :client
|
7
|
+
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.init_with(client)
|
13
|
+
if WeixinAuthorize.weixin_redis.nil?
|
14
|
+
ObjectStore.new(client)
|
15
|
+
else
|
16
|
+
RedisStore.new(client)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def jsticket_expired?
|
21
|
+
raise NotImplementedError, "Subclasses must implement a jsticket_expired? method"
|
22
|
+
end
|
23
|
+
|
24
|
+
def refresh_jsticket
|
25
|
+
set_jsticket
|
26
|
+
end
|
27
|
+
|
28
|
+
def jsticket
|
29
|
+
refresh_jsticket if jsticket_expired?
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_jsticket
|
33
|
+
result = client.http_get("/ticket/getticket", {type: 1}).result
|
34
|
+
client.jsticket = result["ticket"]
|
35
|
+
client.jsticket_expired_at = WeixinAuthorize.calculate_expire(result["expires_in"])
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module WeixinAuthorize
|
3
|
+
module Token
|
4
|
+
class ObjectStore < Store
|
5
|
+
|
6
|
+
def valid?
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def token_expired?
|
11
|
+
# 如果当前token过期时间小于现在的时间,则重新获取一次
|
12
|
+
client.expired_at <= Time.now.to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def refresh_token
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def access_token
|
20
|
+
super
|
21
|
+
client.access_token
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|