Fork me on GitHub

微信开发

微信开发

内网穿透ngrok

首先微信本地开发测试需要有自己的服务器,然而很多新手开发的人并没有自己的服务器,那么,ngrok这款神器就派上用场了。据我所知,官方的ngrok非常不稳定,花了很长时间好不容易连上了,结果一会儿就断开了,死活连不上,设置了dns也没用。好在国内有好心人,提供了国内的服务,而且免费,不用白不用,ngrok.cc,持久连接。需要到它的官网上注册账号并配置二级域名。然后就可以获取到分配的客户端id,就可以连接了。

1
2
# 进入下载的客户端目录
$ ./sunny clientid 客户端id

配置信息

pic1

连接

pic2

进入127.0.0.1:4040还可以查看请求和响应信息

pic3

注:

ngrok需要绑定一个二级域名,需要自己有一个域名,然后到dnspod解析此二级域名,方可生效

微信接入

配置

将刚刚从ngrok获得的二级域名填入到微信公众号里面的url服务器地址栏中,自定义一个token即可。

微信签名认证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const signature = request.query.signature;
const timestamp = request.query.timestamp;
const nonce = request.query.nonce;
const echostr = request.query.echostr;
/* 加密/校验流程如下: */
// 1. 将token、timestamp、nonce三个参数进行字典序排序
const array = [options.token, timestamp, nonce];
array.sort();
const str = array.toString().replace(/,/g, '');
// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
const sha1Code = crypto.createHash('sha1');
const code = sha1Code.update(str, 'utf-8').digest('hex');
// 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
if (code === signature) {
return reply(echostr);
}
return reply('error');

加密解密

这个就不多说了,微信的收发消息都要经过加密,已经有大神写了wechat-crypto,怎么用参考文档,非常简单。

解析xml成json格式

将接收到的xml加密文本进行解密后再解析

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
// 解析前
// <xml>
// <ToUserName><![CDATA[xxx]]></ToUserName>
// <FromUserName><![CDATA[xxx]]></FromUserName>
// <CreateTime>1473232878</CreateTime>
// <MsgType><![CDATA[text]]></MsgType>
// <Content><![CDATA[。]]></Content>
// <MsgId>xxx</MsgId>
// <Encrypt><![CDATA[xxxxx]]></Encrypt>
// </xml>
const parseWechatXML = (xml) => {
if (!xml || typeof xml !== 'string') return {};
const re = {};
const rxml = xml.replace(/^<xml>|<\/xml>$/g, '');
const ms = rxml.match(/<([a-z0-9]+)>([\s\S]*?)<\/\1>/ig);
if (ms && ms.length > 0) {
ms.forEach(t => {
const ms1 = t.match(/<([a-z0-9]+)>([\s\S]*?)<\/\1>/i);
const tagName = ms1[1];
let cdata = ms1[2] || '';
cdata = cdata.replace(/^\s*<!\[CDATA\[\s*|\s*\]\]>\s*$/g, '');
re[tagName] = cdata;
});
}
return re;
};
// 解析后
// { ToUserName: 'xxx',
// FromUserName: 'xxx',
// CreateTime: '1473232878',
// MsgType: 'text',
// Content: '。',
// MsgId: 'xxx',
// Encrypt: 'xxxx' }

回复消息构造

传入对象,得到xml,将xml加密后回复。

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
// 构造前
// {
// ToUserName: 'xxx',
// FromUserName: 'xxx',
// CreateTime: '1473232878',
// MsgType: 'music',
// MsgId: 'xxx',
// Music: {
// Title: 'xx',
// Description: 'xx',
// MusicUrl: 'xxx',
// HQMusicUrl: 'xxx'
// }
// }
const parseObj = module.exports = (xml, obj) => {
let result = xml;
for (const key of Object.keys(obj)) {
result = `${result}<${key}>`;
if (typeof obj[key] === 'object') {
result = parseObj(result, obj[key]);
} else if (key === 'CreateTime' || key === 'MsgId') {
result = `${result}${obj[key]}`;
} else {
result = `${result}<![CDATA[${obj[key]}]]>`;
}
result = `${result}</${key}>`;
}
return result;
};
// 构造后
// <xml>
// <FromUserName><![CDATA[xxx]]></FromUserName>
// <ToUserName><![CDATA[xxx]]></ToUserName>
// <MsgId>xxxxx</MsgId>
// <CreateTime>1473233296</CreateTime>
// <MsgType><![CDATA[music]]></MsgType>
// <Music>
// <Title><![CDATA[xx]]></Title>
// <Description><![CDATA[xx]]></Description>
// <MusicUrl><![CDATA[xx]]></MusicUrl>
// <HQMusicUrl><![CDATA[xx]]></HQMusicUrl>
// </Music>
// </xml>
-------------本文结束感谢您的阅读-------------
如果您觉得受益了,欢迎打赏鼓励。