官方也有一些资料,网上也有不少资料,但是抄起来,不少坑,而且我这边还是python写的,阻力更大,还是我自己总结一下,能给他人带来遍历,那就算欣慰的了。
使用tornado的问题,要注意文件上传到centos上直接覆盖,重启不一定会生效,这个问题花了我2小时。
1 微信公众号-功能设置
注意这里的业务、JS接口安全、网页授权域名,如果加了www.test.cn/mp
路径之类,会提示invalid domain
,所以最好还是根目录
于是,配置路由的时候
(r'/MP_verify_2ge90RVBsxxxx.txt',WxJsCheckHandler),
代码比较简单,也就是将微信授权的文件,返回就可以。
from tornado import web
class WxJsCheckHandler(web.RequestHandler):
def get(self):
self.write("2ge90Rxxxx")
2 微信签名
这个页面上会用到,通过h5页面跳转到小程序需要微信公众号授权
import redis
import string
import random
import time
import json
import requests
import hashlib
from settings import ENV,REDIS_CONFIG,WX_CONFIG
pool = redis.ConnectionPool(host=REDIS_CONFIG[ENV]['host'], port=REDIS_CONFIG[ENV]['port'], db=REDIS_CONFIG[ENV]['db'])
r = redis.StrictRedis(connection_pool=pool)
class WxService():
def __init__(self):
self.wx_url = 'https://api.weixin.qq.com/cgi-bin'
def _create_nonce_str(self):
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(15))
def _create_timestamp(self):
return int(time.time())
def get_token(self):
ACCESS_TOKEN = r.get('wx:ACCESS_TOKEN')
if ACCESS_TOKEN:
return ACCESS_TOKEN
else:
token_url = self.wx_url + '/token?grant_type=client_credential&appid={}&secret={}' \
.format(WX_CONFIG['appid'],WX_CONFIG['secret'])
res = requests.get(token_url)
print('请求微信的token is {}'.format(res.text))
token = json.loads(res.text)
ACCESS_TOKEN = token.get("access_token")
r.set('wx:ACCESS_TOKEN', ACCESS_TOKEN, 7200)
return ACCESS_TOKEN
def get_ticket(self):
ticket = r.get('wx:ticket')
if ticket:
tic = str(ticket, encoding='utf-8')
return tic
else:
token = self.get_token()
ticket_url = self.wx_url + "/ticket/getticket?access_token={}&type=jsapi".format(token)
res = requests.get(ticket_url)
js_ticket = json.loads(res.text)
ticket = js_ticket.get('ticket')
r.set('wx:ticket', ticket, 7200)
return ticket
def sign(self,url):
jsapi_ticket = self.get_ticket()
ret = {
'nonceStr':self._create_nonce_str(),
'jsapi_ticket': jsapi_ticket,
'timestamp':self._create_timestamp(),
'url':url
}
string = '&'.join(['%s=%s' % (key.lower(), ret[key]) for key in sorted(ret)]) # 根据字符的ASCII值进行排序,拼接
ret['signature'] = hashlib.sha1(string.encode('utf-8')).hexdigest() # 对字符串进行sha1加密
return ret
配置路由
(r"/wx/sign",WxSignHandler),
3 前端页面
# web
settings = {
'debug': True,
'static_path':os.path.join(os.path.dirname(__file__), 'static'),
'template_path':os.path.join(os.path.dirname(__file__), "views")
}
# app
app = web.Application([
(r'/MP_verify_2xxxxsxxxx.txt',WxJsCheckHandler),
(r'/js/(.*)',web.StaticFileHandler,{'path':'assets/js'}),
(r"/test/xcx",ToTestXcxHandler),
(r"/test/order/add",OrderHandler),
(r"/wx/sign",WxSignHandler),
web.url(provider.authorize_path,TokenHandler,dict(provider=provider)),
web.url(provider.token_path,TokenHandler,dict(provider=provider)),
],
**settings
)
h5页面,signature,timestamp,nonceStr并不是随便填的,signature是会被微信公众号验签的,这一点特别要注意。
参考了公众号h5网页跳转小程序,但也被误导了,随机数、时间戳与签名是由关系的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎使用</title>
<style>
.home {
position: relative;
width: 100%;
height: 100vh;
}
.logo-wrap {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 400px;
}
.content {
position: absolute;
bottom: 123px;
width: 100%;
display: flex;
justify-content: center;
}
.tip {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: absolute;
bottom: 32px;
width: 100%;
height: 14px;
color: #666;
line-height: 14px;
font-size: 10px;
}
</style>
<script src="/js/jquery-3.6.0.min.js"></script>
<script src="/js/jweixin-1.6.0.js"></script>
</head>
<body>
<div class="home">
<div class="logo-wrap" id="logoUrl">
<img src="/static/logo.jpg" style="width:120px;height: 120px"/>
<div>欢迎使用</div>
</div>
<div class="content" id="content">
</div>
<div class="tip">
<div>微信版本要求为:7.0.12及以上</div>
<div>系统版本要求为:iOS 10.3及以上、Android 5.0及以上</div>
</div>
</div>
<script>
getUrlParam = function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
href = window.location.href.split('#')[0]
console.log(href)
console.log(window.location.href)
nsrsbh = getUrlParam('nsrsbh')
// alert('nsrsbh is '+ nsrsbh)
console.log('nsrsbh is '+ nsrsbh)
$.ajax({
url: '/wx/sign',
type: 'POST',
data: JSON.stringify({
appId: '微信公众号的appid',
url: href
}),
async: false,
dataType: "json",
contentType : "application/json;charset=UTF-8",
success: function (res) {
console.log(JSON.stringify(res.data))
if (res) {
wx.config({
debug: false,
appId: '微信公众号的appid',
timestamp: res.data.timestamp,
nonceStr: res.data.nonceStr,
signature: res.data.signature,
jsApiList: ['scanQRCode'], // 必填,随意一个接口即可
openTagList:['wx-open-launch-weapp'], // 填入打开小程序的开放标签名
})
wx.ready(function (){
console.log("微信环境准备成功");
getContent();
})
wx.error(function (res) {
console.log("微信环境准备失败,原因:"+ res);
});
}
},
error: function(err){
console.log("后台接口获取签名异常,原因:", error);
}
})
getContent = function (){
var color = "#d02329";
var buttonStyle = "width: 343px; height: 200px; color: #FFFFFF; border-radius: 27px; border: 1px solid " + color +"; "
+"background-color: #07ba22; font-size:32px";
var content = '';
var weappurl = "/pages/index/test?nsrsbh="+nsrsbh;
var id = "launch-btn";
content += '<wx-open-launch-weapp id="'+id+'" username="'+"微信小程序的原始ID gh_xxx"+'" path="'+weappurl+'">';
content += '<template>';
content += '<button style="'+buttonStyle+'">打开小程序</button>';
content += '</template>';
content += '</wx-open-launch-weapp>';
$("#content").html(content);
var btn = document.getElementById( 'launch-btn');
btn.addEventListener( 'launch', function(e) {
console.log("调起成功", e.detail);
});
btn.addEventListener( 'error', function(e) {
console.log("调起失败:", e.detail);
});
}
</script>
</body>
</html>
class ToTestXcxHandler(BaseHandler):
def get(self):
self.render("test.html")
4 微信公众号关注自动回复
<a href='https://www.test.cn/test/xcx?nsrsbh=123456789'>欢迎使用</a>