微信的订阅号和服务号开发差别不大,本文同样适用于订阅号的开发。
1. 申请服务号
到微信公众平台申请服务号:https://mp.weixin.qq.com/
微信的权限不时会调整,网上一些文章的描述会过时,最好是登录服务号的后台,查看有哪些权限。很多高级的权限都需要认证的服务号的,认证需要提供公司营业执照等,并每年缴纳一笔费用(印象中是300元/年)。
2. 开发者中心
登录公众平台,做好基本设置(我也不记得有什么东西需要设置了),然后就点击公众平台左下角的“开发者中心”链接查看和设置权限等信息。
其中 开发者文档 会是需要经常查阅的文档,可以收藏到浏览器书签。
3. 接入指南
首先,我们要搭一个PHP的后台,设定一个微信的入口地址,这个地址需要填到公众平台里。微信服务器根据我们填的接口地址,会把服务号的各种请求发向这个地址,PHP程序根据请求的参数返回不同的信息。
这里是官方的接入指南文档,我们要写的第一段代码是响应“第二步:验证服务器地址的有效性”,通过了这个验证,就算完成了PHP接口的雏形。之后就是扩展这段PHP代码,让其可以处理更复杂的请求。官方的文档有一小段PHP代码示例,我这里再提供一段示例(但**不能运行**,因为依赖的相关函数和数据表实现没有提供)。如果你觉得看代码头痛(太多和接口定义相关的逻辑,我自己看也头痛),可以略过。当实现到相关接口的时候再回来看,也许有帮助。
public function index() {
include_once 'weixin.php';
if (!Weixin::checkSignature()) {
return 'Signature validation fail.';
}
// API verificaiton for weixin
if (isset($_GET['echostr']))
return $_GET["echostr"];// get post data, May be due to the different environments
$postStr = file_get_contents("php://input");
if (empty($postStr)) {
exit;
//AccessLog::createLog(0, 'weixin', 0, 'no post data');
}// extract post data
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
// 可以var_dump($postObj),这样就知道微信请求的所有参数了
$openid = $postObj->FromUserName;
$type = $postObj->MsgType;$user = WeixinUser::ofOpenid($openid)->first(); // 查找用户,我们可以设计一个用户数据表,以用户的openid作为识别用户的标记
if (!$user) {
$ret = Weixin::getUserInfo($openid);
$user = WeixinUser::createUser($ret);
}if ($type == 'event') {
$event = $postObj->Event;
$key = $postObj->EventKey;if ($event == 'subscribe') {
// update subscribe status
$user->subscribe = 1;
$user->save();
Weixin::reply($openid, $ret);
} elseif ($event == 'unsubscribe') {
// update subscribe status
$user->subscribe = 0;
$user->save();} elseif ($type == 'text') {
Weixin::reply($openid, '你输入了' . $postObj->Content);
}
}
}
上面的代码引用了一个weixin.php的文件,我简单列一下里面几个函数的实现,完整的实现应该参考开发文档里描述的数据格式。
<?php
class Weixin {
static function checkSignature() {
$signature = filter_input(INPUT_GET, 'signature');
$timestamp = filter_input(INPUT_GET, 'timestamp');
$nonce = filter_input(INPUT_GET, 'nonce');$token = Config::get('app.weixin_token');
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr, SORT_STRING);
$tmpStr = implode($tmpArr);
$tmpStr = sha1($tmpStr);return ($tmpStr == $signature);
}static function getUserInfo($openid) {
$access_token = Weixin::getAccessToken();
if (!$access_token)
return FALSE;$url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' .
$access_token . '&openid=' . $openid . '&lang=zh_CN';
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
$response = curl_exec($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);// Check if login was successful
if ($http_code != 200) {
print curl_error($curl);
return FALSE;
}curl_close($curl);
return json_decode($response);
}
static function getAccessToken($nocache = FALSE) {
// try to get from cache first
$cache_file = '/tmp/weixin_access_token';
if (!$nocache && file_exists($cache_file) &&
$_SERVER['REQUEST_TIME'] - filemtime($cache_file) < 7000) {
static $retry = 0;
$retry++;
if ($retry > 3) {
print "Fail to get access token for 3 times.\n";
return FALSE;
}
$content = file($cache_file);
$token = $content[0];
return rtrim($token);
}$url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&' .
'appid=' . Config::get('app.weixin_appid') .
'&secret=' . Config::get('app.weixin_secret');$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
$response = curl_exec($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);// Check if login was successful
if ($http_code != 200) {
print curl_error($curl);
return FALSE;
}curl_close($curl);
$ret = json_decode($response);
if (isset($ret->access_token)) {
// save cache
$fp = fopen($cache_file, "w");
fwrite($fp, $ret->access_token);
fclose($fp);return $ret->access_token;
} else {
print $response;
return FALSE;
}
}static function reply($openid, $content) {
if (is_array($content)) {
// news type
$ret = '<xml>
<ToUserName><![CDATA[' . $openid . ']]></ToUserName>
<FromUserName><![CDATA[' . Config::get('app.weixin_account') . ']]></FromUserName>
<CreateTime>' . $_SERVER['REQUEST_TIME'] . '</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>' . count($content) . '</ArticleCount>
<Articles>';foreach ($content as $item) {
$ret .= '<item>
<Title><![CDATA[' . $item['title'] . ']]></Title>
<Description><![CDATA[' . $item['description'] . ']]></Description>
<PicUrl><![CDATA[' . $item['picurl'] . ']]></PicUrl>
<Url><![CDATA[' . $item['url'] . ']]></Url>
</item>';
}$ret .= '</Articles></xml>';
echo $ret;
} else {
// text type
$textTpl = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";$ret = sprintf($textTpl, $openid, Config::get('app.weixin_account'),
$_SERVER['REQUEST_TIME'], $content);
echo $ret;
}
}
}
4. 调试
由于没有界面,PHP代码如果出错,需要在web服务器(例如apache)的错误日志(error_log)里找。公众平台提供在线接口调试工具,也可以帮助调查一些问题。
5. 网页授权获取用户基本信息
通常,我们的应用场景是这样的:搞一个服务号,里面有几个按钮,点击部分按钮会回复一些内容,点击部分按钮会跳到网页。有些网页是需要识别用户身份的,就好像用户通过服务号点过来就会自动登录一样。这是怎样做到的呢?认证的微信服务号有一个获取用户信息的功能,比如openid、昵称等。注意,同一个用户访问不同服务号openid是不同的,但访问同一个服务号是唯一的。我们可以把openid映射到网站里的用户(可能需要用户做些操作来关联)。以下是该API的官方文档。
6. 开发手机网站
当获取了用户的ID后,剩下的工作就是纯网站开发了。可以使用jQuery Mobile,做出来的网站体验很像App。网站开发技术参考PHP网站开发指引
评论