企业微信第三方应用配置
TIPS:在我开发之前,在网上找的文档之类的,都是说应用要在套件下创建,但是企业微信已经取消了这个套件,直接就是应用了
创建应用
前期配置
- 想要发布第三方应用,首先要注册成微信服务商
- 完善品牌、官网等信息,提交申请。注:品牌下可以有多个应用,目前企业微信已取消了,套件这个东西
- 登录服务商应用后台 -> 标准应用服务 -> 本地应用 -> 创建应用(创建应用配置分基础信息和开发信息,开发信息是重头戏)
开发配置
捡了一此主要的配置来写:
- 应用主页:这个是在工作台点击应用后,直接跳转的页面。这个url中支持
$CORPID$
,这个会转换成打开应用的企业的corpid
,前端获得这个参数,传给后台处理得到签名(如果想使用jsapi的功能的话)。 - 可信域名:这个就是填写你的网站的域名即可
- 数据回调URL和指令回调URL:这两个在创建应用的时候,微信服务器都会发送一个校验,这个是官网教的处理方法。从校验的角度来说这两个接口是一样的。但是功能上是有区别的。
- 数据回调URL:这个是第三方应用创建完成后,接收企业信息的。这个URL中支持和应用主页一样的
$CORPID$
参数,来区分是哪个企业发来的信息 - 指令回调URL:这个作用比较大,是接收一些授权信息和ticket参数
- 数据回调URL:这个是第三方应用创建完成后,接收企业信息的。这个URL中支持和应用主页一样的
说明:这两个回调URL在验证的时候是GET
请求,在业务处理上是POST
请求。
获取到永久授权码和corpid之后,需要存到数据库中
最后在使用的时候就是这么个过程
- 前端请求后台接口,获取签名,携带参数为
corpid
,这个值前端可以从应用主页的url中就可以获取。另一个参数是当前页面的url
用于后台生成签名。 - 后台接收
corpid
后从数据库中查询出该企业的永久授权码,再结合推送到指令回调的ticket
,三个参数去获取access_token
,详情 - 使用上步获取的
access_token
来获取jsapi_ticket
,详情 - 使用
jsapi_ticket
和url
和随机字符串和时间戳四个参数,去生成签名,详情 - 最后返回前端3个参数,即:签名+生成签名的随机字符串+生成签名的时间戳
前端拿到参数后,进行wx.config
配置后就可以愉快地使用wx的api了。
接口业务代码
只是示意,没写过后台代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163const Express = require('express');
const app = Express();
const bodyParser = require('body-parser');
require('body-parser-xml')(bodyParser);
const axios = require('axios');
app.use(bodyParser.xml());
let ticketCount = 0; // 推送ticket计数器
let suite_access_token = ''; // 全局的suite_access_token,getSuiteAccessToken调用成功后会更新一次
/**
* getSuiteAccessToken:获取suite_access_token
* @param:
* ticket[string]:由指令接口接收到
* */
function getSuiteAccessToken(ticket) {
let data = {
suite_id: 'xxx', // 固定值
suite_secret: 'yyy', // 固定值
suite_ticket: ticket
};
axios.post('https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token', data).then(result => {
suite_access_token = result.body.suite_access_token;
ticketCount = 0;
}).catch(error => {
console.log(error);
// 获取失败,再次获取
getSuiteAccessToken(ticket);
});
}
/**
* getPermanentCode:获取永久授权码
* @param:
* createAuth:临时授权码
* */
function getPermanentCode(createAuth) {
let data = {auth_code: createAuth};
axios.post('https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=' + suite_access_token, data).then(result => {
// 请求永久授权码成功,连接数据库,将企业corpid和永久授权码等信息保存至数据库
});
}
/**
* getAccessToken:根据corpid获取accesstoken
* */
function getAccessToken(corpid) {
// 同样,获取jsApiTicket的AccessToken也是7200s的有效期,也保存到数据库中
// 先查询,如果没有,或者过期 就重新执行一次请求accessToken的过程
return new Promise(async function(resolve, reject) {
let accessToken = 'accessToken'; // 查询
let accessTokenExpiresTime = 'xxx'; // 查询
let isOverdue = new Data() - accessTokenExpiresTime > 7200 * 1000;
if (accessToken && !isOverdue) {
resolve(accessToken);
} else {
let data = {
auth_corpid: corpid,
permanent_code: 'permanent_code' // 需要查询
};
axios.post('https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=' + suite_access_token, data).then(result => {
resolve(result.data.access_token);
});
}
});
}
/**
* getJsApiTicket:获取jsapi ticket
* @param:
* corpid[string]:企业id
* @return:
* ticket[string]:
* */
function getJsApiTicket(corpid) {
// 一个企业的jsApiTicket保存时间为7200s 获得之后就保存到数据库中,有一个字段jsApiTicketExpiresTime记录一下过期时间
// 先从数据库中根据corpid取值,如果没有或者过期则重新请求一次
return new Promise(async function(resolve, reject) {
let jsApiTicket = 'ticket'; // 数据库中查询
let jsApiTicketExpiresTime = 'xxx'; // 数据库中查询
let isOverdue = new Data() - jsApiTicketExpiresTime > 7200 * 1000;
// 没有过期并且存在就使用这个
if (jsApiTicket && !isOverdue) {
resolve(jsApiTicket);
} else {
let accessToken = await getAccessToken();
axios.get('https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=' + accessToken).then(result => {
resolve(result.data.ticket);
});
}
});
}
/**
* getSign:生成签名算法
* */
function getSign() {
// 生成签名算法
}
// 校验接口
app.get('/verify', function (req, res) {
// 创建应用的校验过程
});
// 指令校验接口
app.get('/orderUrl', function (req, res) {
// 转发到 /verify 接口
});
// 数据校验接口
app.get('/dataUrl', function (req, res) {
// 转发到 /verify 接口
});
// 指令接口 可以获取ticket和AuthCode
app.post('/orderUrl', function (req, res) {
// 获取数据类型
let infoType = req.body.InfoType;
if (infoType === 'create_auth') {
// 企业推送授权信息
let authCode = req.body.AuthCode;
getPermanentCode(authCode);
} else if (infoType === 'suite_ticket') {
// 微信后台推送 ticket
ticketCount++;
if (ticketCount >= 10) {
// 10min推送一次,计10次,执行一次获取suite_access_token请求
let ticket = req.body.SuiteTicket;
getSuiteAccessToken(ticket);
}
}
res.send('success');
});
app.get('/sign', async function (req, res) {
let corpid = req.query.corpid;
let url = req.query.url;
let jsApiTicket = await getJsApiTicket(corpid);
let noncestr = 'Wm3WZYTPz0wzccnW'; // 随机生成
let timestamp = 1414587457; // 时间戳
// 根据4个参数生成签名
let sign = getSign(jsApiTicket, url, noncestr, timestamp);
res.send({
sign,
noncestr,
timestamp
});
});
app.listen('8001');