2017 年 Ruby on Rails 5 "生成带参数的二维码"
我这里的业务场景是绑定网站账号和微信账号,方便之后发红包。
"生成带参数的二维码"
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542
代码部分只是生成二维码,没有夹带任何无关的逻辑。欢迎复制粘贴,希望节省了你的时间。
还有一个重点是,这个二维码扫描之后,会自动要求关注对应的微信公众号。这一点是不在程序员控制范围内的。用户关注后,服务器就可以收到扫二维码的事件推送。如果用户已经关注了,那么服务器就直接收到二维码事件推送。
业务场景
代码是写给寓住(yuzhu.me)的,要解决的问题是绑定 网站用户 <-> 微信用户。最终目的是发红包(见另一篇博客讲了发红包的代码实现)
- 用户通过各渠道得知 "填评分领红包" 活动
- 用户访问寓住网站 (https://yuzhu.me)
- 注册账号 -> 然后给公寓打分+提交价格+写评论。(备注:当前11月14号,寓住网站只有自己的注册登录系统,没有任何第三方登录(QQ/微信/微博等)
- 绑定微信号(通过扫二维码的形式)
- 评价审核通过(在寓住后台进行人工审核)
- 用户在审核通过后立刻收到微信红包。
几个重点:
- 微信的逻辑是每个用户一个 OpenID。还可以通过 OpenID 获得用户基础信息,如头像和名字。
但是微信号和手机是不可能拿到的(给没做过的小白科普一下这一点) - 还有就是二维码在扫描后,服务器会收到一个请求。
那么总体逻辑是:
- 生成一个二维码,二维码的数据里带了 user_id。
- 用户扫二维码,事件被推送到服务器,服务器收到 open_id + user_id
- 根据 user_id 找到用户,把 open_id 写进去,这样就建立起了关联。
代码
注意这里代码只是生成二维码的代码,建立账户关联属于自己的业务逻辑,我感觉很简单,没有必要贴出来,所以就不贴了。
这里的大体逻辑是(微信提供的 API 就是这么要求的):
- 获得 access_token
- 通过 access_token 获得 ticket
- 通过 ticket 获得二维码图片地址
Gemfile
gem 'rest-client'
# https://mp.weixin.qq.com/wiki/11/0e4b294685f817b95cbed85ba5e82b8f.html
def get_access_token()
redis = $redis
return redis.get("weixin_access_key") if redis.get("weixin_access_key")
# if not exist in redis
# now we have to send http request to get a access_token from weixin
appid = Rails.application.config.open_weixin_app_id
appsecret = Rails.application.config.open_weixin_app_screct
url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=#{appid}&secret=#{appsecret}"
r = RestClient::Request.execute({method: :get, url: url})
r = JSON.parse(r)
unless r['access_token']
return false
end
redis.set("weixin_access_key", r['access_token'])
redis.expire("weixin_access_key", r['expires_in'].to_i - 60) # 提早 60 秒过期
return r['access_token']
end
# 微信是先拿 ticket 才能拿二维码
def get_qr_ticket(data)
access_token = get_access_token()
data = {
"expire_seconds": 100,
"action_name": "QR_STR_SCENE",
"action_info": {"scene": {"scene_str": data}}
}
url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=#{access_token}"
r = RestClient::Request.execute({method: :post, url: url, payload: data.to_json, content_type: :json})
r = JSON.parse(r)
return r
end
# 返回二维码的 URL
def get_qr_code(data)
ticket_array = get_qr_ticket(data)
ticket = ticket_array['ticket']
url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=#{ticket}"
return url
end
千万注意现在拿 access_token 是要求 IP 白名单的,如果你调用 get_qr_code 获得空,很大可能就是这样原因。你可以把 get_access_token() 里
unless r['access_token']
return false
end
改成 return r 就知道了。返回信息会写 ip not in whitelist 之类的。
还有用了 redis 做缓存。
Gemfile
gem 'redis-rails' # https://github.com/redis-store/redis-rails
gem 'redis', '~>3.2'
我也忘记为啥当初引入这俩了,我猜只引入一个也行,反正现在管用,就懒得纠结这些小事情了。
config/initializers/redis.rb
## Added rescue condition if Redis connection is failed
begin
$redis = Redis.new(:host => Rails.configuration.redis_host, :port => 6379)
rescue Exception => e
puts e
end
app/weixin/weixin.rb
文件的总体结构是这样的:
module Weixin
module_function
def get_access_token()
def get_qr_ticket(data)
def get_qr_code(data)
end
使用方法
我是写在 app/weixin/weixin.rb
里,同时在 config/application.rb
里写了
config.autoload_paths += Dir[Rails.root.join('app', 'weixin')]
达到自动载入的目的。
用法是在 controller 里调用。
先写条路由 config/routes.rb
写一条 get '/get_qr_code' , to: 'api#get_qr_code'
然后在 app/controllers/api_controller
里写
def get_qr_code
data = {
user_id: current_user.id
}
qr = Weixin::get_qr_code(data.to_json)
render plain: qr
return
end
其中 'data' 就是你想带过去的数据。我这里因为只需要绑定用户,所以就给一个 user_id。
这里的 current_user 是来自 devise 。
以上 get_qr_code 会返回一个图片地址。
就是你需要的二维码
https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQFw8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyWGNERkVPNkNmZDAxOGtwYXhxY2wAAgSwmApaAwRkAAAA
访问就会看到了:
完结
有疑问欢迎邮件 guokrfans#gmail.com