微信接口这块的东西不是弄起来不算特别难,封装好了基本都能复用,不过没做过微信开发的同学第一次整起来还是显得有些蛋疼的。
常用的开发主要分4块
1.access_token的获取和存放
:获取access_token的接口是有调用频率的限制的,我们不能每次需要的时候都去调用接口,我们可以将其写入文件,或者写入缓存。access_token的有效期目前为2个小时,每次拿的时候先将我们存放的时间和当前时间作对比,超过7200秒才去请求接口,否则直接拿文件或缓存里面存放的access_token
2.JS-SDK
:这块主要是应用于微信的网页开发,可以直接使用微信提供的包括扫描二维码,上传图片,分享,定位等很多接口。具体可见JS-SDK文档和官方demo。所有需要使用JS-SDK的页面必须先注入配置信息,获取配置信息的接口,需要一个jsapi_ticket,和access_token一样,有效期也是2个小时,这里我们要做的也是存放jsapi_ticket和拿取配置信息注意
:这里需要到微信公众平台里面设置我们的JS接口安全域名,否则是用不了的
3.网页授权
:这里主要是网页授权获取用户信息,然后插入或者更新的user表
- 引导用户进入授权页面同意授权,获取code
- 通过code换取网页授权access_token(与基础支持中的access_token不同)
- 如果需要,开发者可以刷新网页授权access_token,避免过期
- 通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
注意
:这里需要到微信公众平台里面设置我们网页授权域名
4.服务器配置
:这里主要是作为一个回调,可以监听到用户的事件,比如关注,取关,click事件,接受用户发送的信息,这样我们可以跟各种事件或者信息来进行我们的逻辑
1.access_token的获取和存放
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
class Token
{
/**
* 微信配置信息
* @var array
*/
private function createAccessToken()
{
//微信配置
$appid = Yii::$app->params['WeChat']['AppID'];
$corpsecret = Yii::$app->params['WeChat']['AppSecret'];
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appid&secret=$corpsecret";
$accessTokenJSON = file_get_contents($url);
$accessTokenArr = json_decode($accessTokenJSON, true);
$accessTokenArr['timestamp'] = time();
$write = $this->createFile(json_encode($accessTokenArr));
if (isset($accessTokenArr['access_token']) && $write) {
return $accessTokenArr['access_token'];
} else {
die($accessTokenJSON);
}
}
/**
* 读取accessToken
* @return string
*/
public function getAccessToken()
{
$currentTimestamp = time(); // 当前时间戳
$accessTokenJSON = file_get_contents(__DIR__ . '/../wechat/params/token.json');
$accessTokenArr = json_decode($accessTokenJSON, true);
$timestamp = $currentTimestamp - $accessTokenArr['timestamp'];
if ($timestamp < 7200) {// token 有效期2个小时
return $accessTokenArr['access_token'];
} else {//请求接口
return $this->createAccessToken();
}
}
/**
* 创建临时文件
* @param $content
* @return boolean 创建文件是否成功
*/
public function createFile($content)
{
$filename = __DIR__ . '/../wechat/params/token.json';
$file = fopen($filename, "w") or die("Unable to open file!");
$txt = $content;
$count = strlen($content);
if ($count === fwrite($file, $txt)) {// 根据写入内容长度判断文件是否写入,
fclose($file);
return true;
} else {
fclose($file);
return false;
}
}
}
2.JS-SDK
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
87class Jssdk
{
private $appId;
private $appSecret;
public function __construct()
{
$this->appId = Yii::$app->params['WeChat']['AppID'];
$this->appSecret = Yii::$app->params['WeChat']['AppSecret'];
}
public function getSignPackage()
{
$jsapiTicket = $this->getJsApiTicket();
// 注意 URL 一定要动态获取,不能 hardcode.
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
$url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$timestamp = time();
$nonceStr = $this->createNonceStr();
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
$signature = sha1($string);
$signPackage = array(
"appId" => $this->appId,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => $signature,
"rawString" => $string
);
return $signPackage;
}
private function createNonceStr($length = 16)
{
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
private function getJsApiTicket()
{
// jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
$data = json_decode($this->get_ticket_file(), true);
if ($data['expire_time'] < time()) {
$tokenClass = new Token();
$accessToken = $tokenClass->getAccessToken();
$url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken";
$resJson = file_get_contents($url);
$res = json_decode($resJson, true);
if (isset($res['ticket'])) {
$ticket = $res['ticket'];
$data['expire_time'] = time() + 7200;
$data['jsapi_ticket'] = $ticket;
$this->set_php_file(json_encode($data));
} else {
die($resJson);
}
} else {
$ticket = $data['jsapi_ticket'];
}
return $ticket;
}
private function get_ticket_file()
{
return file_get_contents(__DIR__ . "/../wechat/params/jsapi_ticket.json");
}
private function set_php_file($content)
{
$filename = __DIR__ . "/../wechat/params/jsapi_ticket.json";
$fp = fopen($filename, "w");
fwrite($fp, $content);
fclose($fp);
}
}
通过getSignPackage()获取配置信息后就可以在网页部分调用了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script>
wx.config({
debug: true,
appId: '<?php echo $signPackage["appId"];?>',
timestamp: echo $signPackage["timestamp"];,
nonceStr: 'echo $signPackage["nonceStr"];',
signature: 'echo $signPackage["signature"];',
jsApiList: [
// 所有要调用的 API 都要加到这个列表中
]
});
wx.ready(function () {
// 在这里调用 API
'checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
//......
});
</script>
3.网页授权
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
public function oauth()
{
if (!isset($_SESSION['user'])) {
if (isset($_GET['code'])) {//获取到授权code
$code = $_GET['code'];
$appid = Yii::$app->params['WeChat']['AppID'];
$secret = Yii::$app->params['WeChat']['AppSecret'];
//获取access_token和openid
$url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$code&grant_type=authorization_code";
$getTokenJson = file_get_contents($url);
$getTokenArray = json_decode($getTokenJson, true);
$openId = $getTokenArray['openid'];
$accessToken = $getTokenArray['access_token'];
//查询本地数据库判断用户是否存在
$user = UserService::getUserByOpenId($openId);
//请求微信接口获取用户信息
$url = "https://api.weixin.qq.com/sns/userinfo?access_token=$accessToken&openid=$openId&lang=zh_CN";
$getTokenUser = file_get_contents($url);
$getTokenUser = json_decode($getTokenUser, true);
//包括openid,nickname,sex,province,city,country,headimgurl
$data = array(
"subscribe" => 1,
"name" => $getTokenUser["nickname"],
"openid" => $getTokenUser["openid"],
"headimgurl" => $getTokenUser["headimgurl"],
"sex" => $getTokenUser["sex"],
);
//用户存在则更新最新信息
if ($user) {
$user_id = $user['id'];
UserService::udateUser($data, $user_id);
} else {
$user_id = UserService::addUser($data);
}
$_SESSION['user'] = UserService::getUserByUserId($user_id);
} else {//请求oauth2授权
$callback = urlencode("http://" . $_SERVER['HTTP_HOST'] . strip_tags($_SERVER['REQUEST_URI']));
$state = '';
$url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&redirect_uri=$callback&response_type=code&scope=snsapi_userinfo&state=$state#wechat_redirect";
header("Location: $url");
exit;
}
}
return $_SESSION['user'];
}
4.服务器配置
这里可以用到微信公众平台PHP-SDK中的wechat.class.php
库
1 | class Weixin |
这里可以用的2张表菜单表
id | type | name | key | url | pid | order |
---|---|---|---|---|---|---|
1 | view | 菜单一 | http://m.baidu.com | 0 | 1 | |
2 | view | 菜单二 | key | http://m.baidu.com | 0 | 2 |
3 | click | 菜单三 | 0 | 3 | ||
4 | view | 子菜单 | http://m.baidu.com | 3 | 1 |
关键字回复表
id | type | title | description | url | image | key |
---|---|---|---|---|---|---|
1 | news | 菜单三的回复 | 描述 | http://m.baidu.com | 1.jpg | key |
2 | news | 欢迎关注 | 描述 | http://m.baidu.com | 2.jpg | subscribe |
3 | text | 测试 | http://m.baidu.com | 测试 |
可以看到菜单3是click按钮,点击菜单3(key)会回复一个图文消息
关注事件也会回复一个图文消息
给公众号发测试
会回复一条文字消息
大致就这些了,如有错误欢迎指正
具体可参考微信开发者文档