# Web微信API文档
## 1、登录
Web微信登录页面是[https://wx.qq.com](https://wx.qq.com),以下内容以此页开始。
### 1.1、获取登录uuid
进入Web微信登录页面后,在开发者工具中查看网络请求情况,可以发现在二维码图片加载之前,浏览器发送了一次 GET 请求:
```
https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=1532078129220
```
该请求用于获取登录uuid,分析发现有如下URL参数:
|参数名|示例值|描述|
|---|---|---|
|appid|wx782c26e4c19acffb|这个值不变,表示来自Web版微信;|
|redirect_uri|https%3A%2F%2Fwx2.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage|跳转页面;非必须值;|
|fun|new|固定值;|
|lang|zh_CN|语言;zh_CN表示中文;|
|_|1532078129220|13位时间戳;|
然后服务器返回数据:
```
window.QRLogin.code = 200; window.QRLogin.uuid = "wcXrINkB9Q==";
```
- window.QRLogin.code的值为200时表示成功,为其它值均表示失败。
- window.QRLogin.uuid的值为获取到的uuid,每次刷新会获取到不同的uuid,实际上uuid是服务端用来标识每一次登录的。
### 1.2、获取二维码
接着会发现如下 GET 请求:
```
https://login.weixin.qq.com/qrcode/wcXrINkB9Q==
```
该url为二维码图片的链接,url尾部的字符串即是上面获取到的uuid。
### 1.3、等待确认登录
在获取二维码的请求之后,会发现如下 GET 请求:
```
https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=wcXrINkB9Q==&tip=0&r=1223005686&_=1532080293141
```
这是等待扫描登录并获取跳转url的请求。如果没有及时扫描二维码,一定时间后该请求会重复出现,表示浏览器在轮询请求。
URL参数分析:
|数名|示例值|描述|
|---|---|---|
|loginicon|true|固定值;|
|uid|wcXrINkB9Q==|同前面的uuid;|
|tip|0或1|0表示等待用户扫码确认;1表示等待用户直接确认,用于push登陆时;|
|r|1223005686|10位随机数|
|_|1532080293141|13位时间戳;|
如果没有及时扫描二维码,服务器返回数据:
```
window.code=408;
```
- 表示没有轮询到认证信息,此时需要再次发送该请求。
如果一直没有扫描二维码,服务器返回数据:
```
window.code=400;
```
- 表示该次登录uuid失效,即二维码失效,此时会停止轮询,需要重新获取uuid才能继续。
及时扫码认证之后,在点击确认登录之前,服务器返回数据:
```
window.code=201;
```
- 表示微信APP端已经扫码,但还没有点击确认。
及时扫码并点击确认之后,服务器返回:
```
window.code=200;
window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A51bGY49nxxxxxxx1wNPC3@qrticket_0&uuid=wcXrINkB9Q==&lang=zh_CN&scan=1532083897";
```
- window.code的值为200表示APP端认证成功。
- window.redirect_uri为需要跳转的url。
### 1.4、获取登录认证码
微信APP端认证后,会产生的新的 GET 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A51bGY49nxxxxxxx1wNPC3@qrticket_0&uuid=wcXrINkB9Q==&lang=zh_CN&scan=1532083897&fun=new&version=v2
```
我们发现该请求即是上一步认证成功之后得到的redirect_uri再拼上&fun=new&version=v2。
该请求发送之后,其它web或pc端登录的微信会被踢下线。
URL参数分析:
|数名|示例值|描述|
|---|---|---|
|ticket|A51bGY49nxxxxxxx1wNPC3@qrticket_0|保存不变即可;|
|uuid|wcXrINkB9Q==|同前面的uuid;|
|lang|zh_CN|语言;zh_CN表示中文;|
|scan|1532083897|用户扫描的时间戳;|
|fun|new|固定值;|
|version|v2|固定值;|
服务器返回数据:
```XML
0
@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp
8rwxxxxxxxxxHq2P
26xxx7
AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=
1
```
***(为了个人隐私,以上返回数据有打码)***
- ret表示请求返回状态,0表示成功。
- skey、wxsid和wxuin都是具体微信用户的信息,不会变化,在后续的通讯中需要用到。
- pass_ticket在初始化页面时需要用到。
### 1.5、退出登录
如果点击退出,我们会发现先后出现如下两个 POST 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=0&skey=%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1&skey=%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp
```
这两个请求唯一的区别就是type的值,先是0再是1。
URL参数分析:
|数名|示例值|描述|
|---|---|---|
|redirect|1|固定值;|
|type|0或1|0或1,固定值;|
|skey|%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|登录时获取的;需要urlencode。|
FORM表单参数分析:
|参数名|示例值|描述|
|---|---|---|
|sid||登录时获取的;|
|uin||登录时获取的;|
服务器返回数据:
- 该请求会导致页面跳转,没有返回数据。
### 1.6、push登录
点击退出后,页面会出现点击直接登录的页面,点击登录之后,会发现产生一个叫webwxpushloginurl的 GET 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxpushloginurl?uin=26xxx7
```
该请求是不扫码获取uuid的方式,仅用在手动退出登录之后,uuid获取之后还是继续1.3的等待确认。
URL参数分析:
|数名|示例值|描述|
|---|---|---|
|uin|26xxx7|同前面获取的uin;|
服务器返回数据:
```JavaScript
{
"ret": "0",
"msg": "all ok",
"uuid": "A_1_xxxw=="
}
```
- ret为0表示成功,为1表示uin错误等失败。
- uuid为新的uuid。经过反复“退出-push登录-退出-push登录”测试发现,全程uin保持不变,uuid会变化。
## 2、数据同步
### 2.1、页面初始化
上面已经拿到了用户认证码,接着需要登录并初始化页面。
初始化页面就是向服务端请求常用的联系人等信息,如下 POST 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=1219172280&lang=zh_CN&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=
```
URL参数分析:
|数名|示例值|描述|
|---|---|---|
|r|1219172280|10位随机数;|
|lang|zh_CN|语言;zh_CN表示中文;|
|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=|上一步获取到的;|
POST的数据(JSON):
```JavaScript
{
"BaseRequest": {
"DeviceID": "e659xxxxxxxx3006",
"Sid": "8rwxxxxxxxxxHq2P",
"Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin": "26xxx7"
}
}
```
POST参数分析:
|数名|示例值|描述|
|---|---|---|
|BaseRequest.DeviceID|e659xxxxxxxx3006|设备ID,e拼上15位随机串;|
|BaseRequest.Sid|rwxxxxxxxxxHq2P|上一步获取到的wxsid;|
|BaseRequest.Skey|crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|上一步获取到的skey;|
|BaseRequest.Uin|6xxx7|上一步获取到的wxuin;|
服务器返回数据:
```JavaScript
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"Count": 11,
"ContactList": [{
"Uin": 0,
"UserName": "filehelper",
"NickName": "...",
"HeadImgUrl": "...",
"ContactFlag": 5,
"MemberCount": 0,
"MemberList": [],
"RemarkName": "",
"HideInputBarFlag": 0,
"Sex": 0,
"Signature": "",
"VerifyFlag": 0,
"OwnerUin": 0,
"PYInitial": "WJCSZS",
"PYQuanPin": "wenjianchuanshuzhushou",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"StarFriend": 0,
"AppAccountFlag": 0,
"Statues": 0,
"AttrStatus": 0,
"Province": "",
"City": "",
"Alias": "",
"SnsFlag": 0,
"UniFriend": 0,
"DisplayName": "",
"ChatRoomId": 0,
"KeyWord": "fil",
"EncryChatRoomId": "",
"IsOwner": 0
}],
"SyncKey": {
"Count": 4,
"List": [{
"Key": 1,
"Val": 704152857
}, {
"Key": 2,
"Val": 704152894
}, {
"Key": 3,
"Val": 704152870
}, {
"Key": 1000,
"Val": 1532075042
}]
},
"User": {
...
},
"ChatSet": "...",
"SKey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"ClientVersion": 3697797,
"SystemTime": 1132000000,
"GrayScale": 1,
"InviteStartCount": 40,
"MPSubscribeMsgCount": 0,
"MPSubscribeMsgList": [],
"ClickReportInterval": 600000
}
```
- BaseResponse.Ret表示请求返回状态,0表示成功。
- ContactList为最近联系人列表。联系人的UserName以一个@开头为好友,两个@为群组。
- MPSubscribeMsg为公众号订阅消息。
- User是当前登录账户信息。
### 2.2、开启消息状态通知
接着需要开启消息状态通知。我们可以看到如下 POST 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify?lang=zh_CN&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=
```
URL参数分析:
|数名|示例值|描述|
|---|---|---|
|lang|zh_CN|语言;zh_CN表示中文;|
|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=|同前面;|
POST的数据(JSON):
```JavaScript
{
"BaseRequest": {
"DeviceID": "e81xxxxxxxxx7686",
"Sid": "8rwxxxxxxxxxHq2P",
"Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin": "26xxx7"
},
"ClientMsgId": 1532xxxxx3374,
"Code": 3,
"FromUserName": "@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab",
"ToUserName": "@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab"
}
```
POST参数分析:
|参数名|示例值|描述|
|---|---|---|
|BaseRequestB|{
"DeviceID":"e81xxxxxxxxx7686",
"Sid":"8rwxxxxxxxxxHq2P",
"Skey":"@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin":"26xxx7"
}|每一次POST请求都会发送BaseRequest;其中DeviceID是字母e拼上15位随机字符串,其余三个都是前面获取到的认证信息。|
|ClientMsgId|1532xxxxx3374|13位时间戳;|
|Code|3|固定值;|
|FromUserName|@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab|页面加载时获取到的User.UserName;|
|ToUserName|@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab|同FromUserName;|
服务器返回数据:
```javascript
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"MsgID": "2271xxxxxxxxxxxxx44"
}
```
- BaseResponse.Ret表示请求返回状态,0表示成功。
### 2.3、服务端状态同步
继续观察,我们没间隔一定的时间,浏览器会想服务器发送一个重复的 GET 请求:
```
https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=1532145313248&skey=%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp&sid=8rwxxxxxxxxxHq2P&uin=26xxx7&deviceid=e35077xxxx772148&synckey=1_704152857%7C2_704152996%7C3_704152955%7C1000_1532127722&_=1532145290777
```
该请求是轮询向服务器获取最新在线、消息等状态的。
URL参数分析:
|数名|示例值|描述|
|---|---|---|
|r|1532145313248|13位时间戳;|
|skey|%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|同前面;需要urlencode;|
|sid|8rwxxxxxxxxxHq2P|同前面;|
|uin|26xxx7 同前面;|
|deviceid|e35077xxxx772148|设备ID,e拼上15位随机串;|
|synckey|1_704152857%7C2_704152996%7C3_704152955%7C1000_1532127722|由初始化登录页面信息返回串中Sync的list列表组成;需要urlencode;|
|_|1532145290777|13位时间戳;|
服务器返回数据:
```
window.synccheck={retcode:"0",selector:"2"}
```
返回数据分析:
|名称|示例值|描述|
|---|---|---|
|retcode|0|表示当前在线状态。
0:正常
1100:失败/登出微信
1101:从其它设备登录微信|
|selector|2|表示消息状态。
0:正常
2:有新消息
4:目前发现修改了联系人备注会出现
7:手机操作了微信|
## 3、联系人管理
### 3.1、获取全部联系人列表
接着可以看到获取全部联系人列表的 GET 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D&r=1532190636970&seq=0&skey=@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp
```
URL参数分析:
|参数名|示例值|描述|
|---|---|---|
|lang|zh_CN|语言;|
|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D|同前面获取到的;|
|r|1532190636970|13位时间戳;|
|seq|0|固定值;|
|skey|@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|同前面获取到的;|
服务器返回数据:
```javascript
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"MemberCount": 1,
"MemberList": [{
"Uin": 0,
"UserName": "weixin",
"NickName": "微信团队",
"HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=1&username=weixin&skey=@crypt_4fb399da_91a345e0be9deca5d61b183fc36a3a93",
"ContactFlag": 1,
"MemberCount": 0,
"MemberList": [],
"RemarkName": "",
"HideInputBarFlag": 0,
"Sex": 0,
"Signature": "微信团队官方帕啷",
"VerifyFlag": 56,
"OwnerUin": 0,
"PYInitial": "WXTD",
"PYQuanPin": "weixintuandui",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"StarFriend": 0,
"AppAccountFlag": 0,
"Statues": 0,
"AttrStatus": 4,
"Province": "",
"City": "",
"Alias": "",
"SnsFlag": 0,
"UniFriend": 0,
"DisplayName": "",
"ChatRoomId": 0,
"KeyWord": "wei",
"EncryChatRoomId": "",
"IsOwner": 0
}],
"Seq": 0
}
```
- BaseResponse.Ret为0表示请求成功。
- MemberCount表示联系人总数。
- MemberList表示联系人列表。这个列表中同时包含好友、群组、公众号。好友和公众号是通过ContactFlag来区分的,1是好友,2是群组,3是公众号。
- User内部字段介绍:
```javascript
"Uin": 0,
"UserName": 用户名称,一个"@"为好友,两个"@"为群组
"NickName": 昵称
"HeadImgUrl":头像图片链接地址
"ContactFlag": 1-好友, 2-群组, 3-公众号
"MemberCount": 成员数量,只有在群组信息中才有效,
"MemberList": 成员列表,
"RemarkName": 备注名称
"HideInputBarFlag": 0,
"Sex": 性别,0-未设置(公众号、保密),1-男,2-女
"Signature": 公众号的功能介绍 or 好友的个性签名
"VerifyFlag": 0,
"OwnerUin": 0,
"PYInitial": 用户名拼音缩写
"PYQuanPin": 用户名拼音全拼
"RemarkPYInitial":备注拼音缩写
"RemarkPYQuanPin": 备注拼音全拼
"StarFriend": 是否为星标朋友 0-否 1-是
"AppAccountFlag": 0,
"Statues": 0,
"AttrStatus": 119911,
"Province": 省
"City": 市
"Alias":
"SnsFlag": 17,
"UniFriend": 0,
"DisplayName": "",
"ChatRoomId": 0,
"KeyWord":
"EncryChatRoomId": ""
```
### 3.2、批量获取指定用户信息
然后可以看到批量获取指定联系人信息的 POST 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=1532246093335&lang=zh_CN&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D
```
该请求用于批量获取好友信息,或者批量获取群聊中的成员信息。
URL参数分析:
|参数名|示例值|描述|
|---|---|---|
|type|ex|不知道什么意思;|
|r|1532246093335|13位时间戳;|
|lang|zh_CN|语言;|
|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D|前面获取到的;|
POST的数据(JSON):
```javascript
{
"BaseRequest": {
"DeviceID": "e81xxxxxxxxx7686",
"Sid": "8rwxxxxxxxxxHq2P",
"Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin": "26xxx7"
},
"Count": 2,
"List": [
{
"EncryChatRoomId": "",
"UserName": "@@76feaa87c63576aaxxxxxxxxeaaa7030c60a09d33"
},
{
"ChatRoomId": "",
"UserName": "@@55664b9ab69bbe5e6aaxxxxxxxxx778c17e11e9dab09c9a1"
}
]
}
```
POST参数分析:
|参数名|示例值|描述|
|---|---|---|
|BaseRequest||基本请求参数,同前面;|
|Count||要进行批量查询的用户数;|
|List||批量指定要查询的UserName的列表;如果要获取好友信息,EncryChatRoomId或ChatRoomId为空,UserName为好友的UserName;如果要获取群聊成员的信息,EncryChatRoomId或ChatRoomId为群聊的UserName,UserName为成员的UserName。|
服务器返回数据:
```javascript
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"Count": 2,
"ContactList": [{
"Uin": 0,
"UserName": "@@76feaa87c635763438059aaeaaa7030c60a09d33",
"NickName": "一啣群)",
"HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgetheadimg?seq=704138898&username=@@76feaa87c635763438059aaeaaa7030c60a09d33&skey=",
"ContactFlag": 3,
"MemberCount": 80,
"MemberList": [{
"Uin": 0,
"UserName": "@3a13860046f9f5cac66faba26f2bd0537f535",
"NickName": "龎",
"AttrStatus": 2147437,
"PYInitial": "",
"PYQuanPin": "",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"MemberStatus": 0,
"DisplayName": "",
"KeyWord": ""
}, ...],
"RemarkName": "",
"HideInputBarFlag": 0,
"Sex": 0,
"Signature": "",
"VerifyFlag": 0,
"OwnerUin": 0,
"PYInitial": "",
"PYQuanPin": "",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"StarFriend": 0,
"AppAccountFlag": 0,
"Statues": 0,
"AttrStatus": 0,
"Province": "",
"City": "",
"Alias": "",
"SnsFlag": 0,
"UniFriend": 0,
"DisplayName": "",
"ChatRoomId": 0,
"KeyWord": "",
"EncryChatRoomId": "@5fd8877457351e3a697",
"IsOwner": 0
}, {
"Uin": 0,
"UserName": "@f7dee38d1c3626c0e421c1f66deac0906559ac",
"NickName": "尕娅",
"HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=686458815&username=@f7dee38d1c3626c0e421c1f66deac0906559ac&skey=",
"ContactFlag": 259,
"MemberCount": 0,
"MemberList": [],
"RemarkName": "啢潧",
"HideInputBarFlag": 0,
"Sex": 2,
"Signature": "ä½ è",
"VerifyFlag": 0,
"OwnerUin": 0,
"PYInitial": "XY",
"PYQuanPin": "",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"StarFriend": 0,
"AppAccountFlag": 0,
"Statues": 0,
"AttrStatus": 1047,
"Province": "å京",
"City": "",
"Alias": "",
"SnsFlag": 49,
"UniFriend": 0,
"DisplayName": "",
"ChatRoomId": 0,
"KeyWord": "",
"EncryChatRoomId": "0",
"IsOwner": 0
}]
}
```
- ContactList即是获取到的指定的联系人信息。
### 3.3、修改联系人备注
当我们修改联系人备注的时候,会发现浏览器发送了如下 POST 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog
```
POST的数据(JSON):
```javascript
{
"BaseRequest": {
"DeviceID": "e81xxxxxxxxx7686",
"Sid": "8rwxxxxxxxxxHq2P",
"Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin": "26xxx7"
},
"CmdId": 2,
"RemarkName": "新的备注名",
"UserName": "@07a2e8b4a1f98eb78a7xxxxxxxxxxxx5b7bc4908376xxx05917b"
}
```
POST参数分析:
|参数名|示例值|描述|
|---|---|---|
|BaseRequest||基本请求参数,同其它;|
|CmdId|2|固定值;有可能还有其它值来表示不同类型操作。|
|RemarkName||新的备注名;|
|UserName||要修改的联系人的UserName;|
服务器返回数据:
```javascript
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
}
}
```
- BaseResponse.Ret表示操作成功。
## 4、收发消息
### 4.1、从服务端拉取新消息
我们发现2.4中的轮询请求每一次返回的数据中selector为2或者6时,都会发生如下的 POST 请求来从服务端获取新消息:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=8rwxxxxxxxxxHq2P&skey=@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D
```
URL参数分析:
|参数名|示例值|描述|
|---|---|---|
|sid|8rwxxxxxxxxxHq2P|同前面获取的;|
|skey|@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|同前面获取的;|
|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D|同前面获取的;|
POST的数据(JSON):
```javascript
{
"BaseRequest": {
"DeviceID": "e2089xxxxx902803",
"Sid": "8rwxxxxxxxxxHq2P",
"Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin": 26xxx7
},
"SyncKey": {
"Count": 4,
"List": [{
"Key": 1,
"Val": 704153081
},
{
"Key": 2,
"Val": 704153167
},
{
"Key": 3,
"Val": 704153152
},
{
"Key": 1000,
"Val": 1532180042
}
]
},
"rr": 1112687259
}
```
POST参数分析:
|参数名|示例值|描述|
|---|---|---|
|BaseRequest|{
"DeviceID": "e81xxxxxxxxx7686",
"Sid": "8rwxxxxxxxxxHq2P",
"Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin": "26xxx7"
}|每一次POST请求都会发送
BaseRequest;其中DeviceID是字母e拼上15位随机字符串,其余三个都是前面获取到的认证信息。|
|SyncKey|{
"Count": ...,
"List": [...]
}|前一次获取到的SyncKey;首次是初始化数据时获取的,zhiy之后都是通过本请求更新;|
服务端返回数据:
```javascript
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"AddMsgCount": 1,
"AddMsgList": [{
"MsgId": "462399962004142457",
"FromUserName": "@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab",
"ToUserName": "@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab",
"MsgType": 51,
"Content": "",
"Status": 3,
"ImgStatus": 1,
"CreateTime": 1532190642,
"VoiceLength": 0,
"PlayLength": 0,
"FileName": "",
"FileSize": "",
"MediaId": "",
"Url": "",
"AppMsgType": 0,
"StatusNotifyCode": 4,
"StatusNotifyUserName": "...",
"RecommendInfo": {
"UserName": "",
"NickName": "",
"QQNum": 0,
"Province": "",
"City": "",
"Content": "",
"Signature": "",
"Alias": "",
"Scene": 0,
"VerifyFlag": 0,
"AttrStatus": 0,
"Sex": 0,
"Ticket": "",
"OpCode": 0
},
"ForwardFlag": 0,
"AppInfo": {
"AppID": "",
"Type": 0
},
"HasProductId": 0,
"Ticket": "",
"ImgHeight": 0,
"ImgWidth": 0,
"SubMsgType": 0,
"NewMsgId": 462399962004142457,
"OriContent": "",
"EncryFileName": ""
}],
"ModContactCount": 0,
"ModContactList": [],
"DelContactCount": 0,
"DelContactList": [],
"ModChatRoomMemberCount": 0,
"ModChatRoomMemberList": [],
"Profile": {
"BitFlag": 0,
"UserName": {
"Buff": ""
},
"NickName": {
"Buff": ""
},
"BindUin": 0,
"BindEmail": {
"Buff": ""
},
"BindMobile": {
"Buff": ""
},
"Status": 0,
"Sex": 0,
"PersonalCard": 0,
"Alias": "",
"HeadImgUpdateFlag": 0,
"HeadImgUrl": "",
"Signature": ""
},
"ContinueFlag": 0,
"SyncKey": {
"Count": 7,
"List": [{
"Key": 1,
"Val": 704153081
}, {
"Key": 2,
"Val": 704153169
}, {
"Key": 3,
"Val": 704153152
}, {
"Key": 11,
"Val": 704152941
}, {
"Key": 201,
"Val": 1532190642
}, {
"Key": 1000,
"Val": 1532190561
}, {
"Key": 1001,
"Val": 1532180114
}]
},
"SKey": "",
"SyncCheckKey": {
"Count": 7,
"List": [{
"Key": 1,
"Val": 704153081
}, {
"Key": 2,
"Val": 704153169
}, {
"Key": 3,
"Val": 704153152
}, {
"Key": 11,
"Val": 704152941
}, {
"Key": 201,
"Val": 1532190642
}, {
"Key": 1000,
"Val": 1532190561
}, {
"Key": 1001,
"Val": 1532180114
}]
}
}
```
- AddMsgCount为新消息数目。
- AddMsgList为新消息列表。
- MsgType说明:
- 1:文本消息
- 3:图片消息
- 34:语音消息
- 37:VERIFYMSG(验证消息)
- 40:POSSIBLEFRIEND_MSG(可能的朋友的消息)
- 42:共享名片
- 43:视频通话消息
- 47:动画表情
- 48:位置消息
- 49:分享链接
- 50:VOIPMSG(VoIP消息)
- 51:微信初始化消息
- 52:VOIPNOTIFY(VoIP通知)
- 53:VOIPINVITE(VoIP邀请)
- 62:小视频
- 9999:SYSNOTICE(系统通知)
- 10000:系统消息
- 10002:撤回消息
- SyncKey会暂存起来作为下一次请求的参数。
### 4.2、发送文本消息
打开一个聊天界面,向好友发送一条消息,会发现产生如下 POST 请求:
```
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D
```
URL参数分析:
|参数名|示例值|描述|
|---|---|---|
|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D|同前面获取的;|
POST的数据(JSON):
```javascript
{
"BaseRequest": {
"DeviceID": "e57xxxxxxx94xx27",
"Sid": "8rwxxxxxxxxxHq2P",
"Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin": "26xxx7"
},
"Msg": {
"ClientMsgId": "15321964202620841",
"Content": "这里是消息内容",
"FromUserName": "@da9d7631177b3c54492954010eb7",
"LocalID": "15321964202620841",
"ToUserName": "@d946a2006c12e85deda45c26b8cd42dbd3f03fa67be",
"Type": 1
},
"Scene": 0
}
```
POST参数分析:
|Key|示例值|描述|
|---|---|---|
|BaseRequest||同上;|
|Msg.ClientMsgId||同LocalID;|
|Msg.Content||消息内容;消息为媒体时,该值为媒体ID。|
|Msg.FromUserName||发送用户;|
|Msg.LocalID||13位时间戳+4位随机数;|
|Msg.ToUserName||接收用户;|
|Msg.Type|1|消息类型;同3.1中接收的消息类型;|
|Scene|0|固定值;|
服务端返回数据:
```javascript
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"MsgID": "7650884298732299102",
"LocalID": "15321964202620841"
}
```
- LocalID是发送时指定的LocalID。
- MsgID是服务端的消息ID。
### 4.3、上传媒体到服务器
如果我们发送一个图片、文件等媒体类消息,会看到在发送消息请求前有如下 POST 请求:
```
https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json
```
若文件较小,则只会产生一个该请求,若文件较大,则会产生多个该请求。多个请求表示以分片的方式发送文件数据,分片大小约为524288。
FORM表单参数分析:
|参数名|示例值|描述|
|---|---|---|
|~~id~~|WU_FILE_0|页面文件组件ID;非必须;|
|~~name~~|bd00c8eed5c2cd522e69f38317b46903.jpg|文件名;非必须;|
|~~type~~|image/jpeg|文件的MimeType;非必须;|
|~~lastModifieDate~~||文件最后修改时间;非必须;|
|~~size~~|99785|文件大小;非必须;|
|chunks|3|文件分片数量;文件较大需要分片时必须;|
|chunk|0|文件分片序号;从0开始;文件较大需要分片时必须;|
|mediatype|pic|文件类型;图片为pic,视频为video,其它文件为doc;必须;|
|uploadmediarequest||JSON字符串;必须;|
|webwx_data_ticket||cookie中的数据;必须;|
|pass_ticket||登录时获取的;必须;|
|filename||文件二进制数据;必须;|
uploadmediarequest结构示例:
```json
{
"UploadType": 2,
"BaseRequest": {
"Uin": 1245,
"Sid": "zxaODbK4ed",
"Skey": "@crypt_4f399da_3c7a87e93b7872bce",
"DeviceID": "e69688819413"
},
"ClientMediaId": 153686311,
"TotalLen": 87508,
"StartPos": 0,
"DataLen": 87508,
"MediaType": 4,
"FromUserName": "@6feb88ee01067121479f686",
"ToUserName": "@6feb88ee0106f67121479f686",
"FileMd5": "717d1dbb9833c7cdbdd09ac"
}
```
服务端返回数据:
```json
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"MediaId": "@crypt_17ab5.................39bd9cfa9ab",
"StartPos": 99785,
"CDNThumbImgHeight": 56,
"CDNThumbImgWidth": 100,
"EncryFileName": "bd00c8e.....38317b46903.jpg"
}
```
上传成功后,返回数据中会有MediaId,如果是分片上传的,则最后一个请求会返回MediaId。
### 4.4、发送图片消息
发送图片后,紧接着4.3的是如下POST请求:
```
https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg?fun=async&f=json&lang=zh_CN&pass_ticket=xFAjPcq0eXh.......TYHHdHgVL52eHo%253D
```
URL参数分析:
|数名|示例值|描述|
|---|---|---|
|pass_ticket|xFAjPcq0eXh.......TYHHdHgVL52eHo%253D|同前面获取的;|
POST的数据(JSON):
```json
{
"BaseRequest": {
"Uin": 16...245,
"Sid": "KvCP..../TNqh8",
"Skey": "@crypt_4fb399da......9f1b3d997c4b5eef82",
"DeviceID": "e2825...3652448"
},
"Msg": {
"Type": 3,
"MediaId": "@crypt_17ab5.................39bd9cfa9ab",
"Content": "",
"FromUserName": "@3641f45.......69bed57642",
"ToUserName": "@3641f454....9bed57642",
"LocalID": "153691....0527",
"ClientMsgId": "1536....390527"
},
"Scene": 0
}
```
POST参数分析:
|Key|示例值|描述|
|---|---|---|
|BaseRequest||同上;|
|Msg.ClientMsgId||同LocalID;|
|Msg.Content||消息内容;消息为媒体时,该值为媒体ID。|
|Msg.FromUserName||发送用户;|
|Msg.LocalID||13位时间戳+4位随机数;|
|Msg.ToUserName||接收用户;|
|Msg.Type|3|消息类型;同3.1中接收的消息类型;|
|Msg.MediaId||4.3接口中返回的MediaId;|
|Scene|0|固定值;|
服务端返回数据:
```json
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"MsgID": "28832......8291836",
"LocalID": "1536.....54390527"
}
```
Web微信发送图片,需要先调4.3的接口将媒体文件上传到服务器,然后再调4.4的接口发送图片的相关信息。