weixin_authorize_superayi 1.6.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|