主题:
通过微信公众号API接口实现微信的关注、取消关注、定位、JSSDK定位。
实现效果:
用户关注后,将关注者信息存入数据库:
取消关注后,软删除(sub_status值变为0):
定位(关注时即可获取定位信息并存入数据库):
JSSDK定位:
控制器Wechat.php实例
<?php
namespace app\index\controller;
use think\Controller;
class Wechat extends Controller
{
// 微信signature签名校验
public function __construct() {
parent::__construct();
$this->model = model('Wechat');
}
public function index() {
$check = $this->model->check();
// 判断模型中返回值
if(!$check) {
exit('signature error');
}
// 接收微信请求数据流对象
$xmldata = file_get_contents("php://input");
// 转换获取到的xml字符串为SimpleXMLElement对象,然后输出对象的键和元素
$postObj = simplexml_load_string($xmldata, 'SimpleXMLElement', LIBXML_NOCDATA);
// 将对xml象转化为一个数组
$data = (array)$postObj;
// 事件推送,企业级开发中必须做数据真实性判断再执行下一步
if(isset($data['MsgType']) && $data['MsgType'] == 'event') {
// 关注
if($data['Event'] == 'subscribe') {
$this->model->subscribe($data);
}
// 取消关注
if($data['Event'] == 'unsubscribe') {
$this->model->unsubscribe($data);
}
// 定位
if($data['Event'] == 'LOCATION') {
$this->model->location($data);
// file_put_contents('D://location.txt', var_export($data, true));
exit('success');
}
}
// 消息推送
if(isset($data['MsgType']) && $data['MsgType'] == 'text') {
$this->robot($data);
exit('success');
}
exit(input('get.echostr'));
}
// 获取校验access_token(区别于网页授权access_token)
public function get_access_token() {
$access_token = $this->model->access_token();
return $access_token;
}
// 微信网页授权
public function auth() {
// 如果正式公众号必须使用备案域名,并配置到公众号回调域名中
$redirect_uri = config('app.myurl'). 'index.php/index/wechat/userinfo';
// 第一步:用户同意授权,获取code
$url_code = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='. config('app.appid'). '&redirect_uri='. urlEncode($redirect_uri).'&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect';
// 获取$url的Loation请求头信息
header('Location:'. $url_code);
}
// 显示用户信息
public function userinfo() {
// 获取code
$code = input('get.code');
// 第二步:通过code换取网页授权access_token(注意这个和之前的普通access_token完全不一样)
$res = $this->model->auth_access_token($code, false);
$auth_access_token = $res['access_token'];
$openid = $res['openid'];
// 第三步:拉取用户信息(需要scope为snsapi_userinfo)
$userinfo = $this->model->get_userinfo($auth_access_token, $openid);
dump($userinfo);
}
// 获取用户地理位置,JSSDK方式
public function location() {
$data['appid'] = config('app.appid');
$data['timestamp'] = time();
$data['nonceStr'] = md5(time().rand(1,999));
// 生成签名
// 1.获取jsapi_ticket
$access_token = $this->model->access_token();
$jsapi_ticket = $this->model->jsapi_ticket($access_token);
// 2.构造签名字符串
$params['noncestr'] = $data['nonceStr'];
$params['jsapi_ticket'] = $jsapi_ticket;
$params['timestamp'] = $data['timestamp'];
$params['url'] = config('app.myurl'). 'index.php/index/wechat/location';
ksort($params);
// 防止url字符转义
$str = urldecode(http_build_query($params));
// 3.生成签名
$data['signature'] = sha1($str);
// 将所有数据打包成一个数组传入视图,默认传入方法名同名视图
return $this->fetch('', $data);
}
}
运行实例 »
点击 "运行实例" 按钮查看在线实例
模型Wechat.php实例
<?php
namespace app\index\model;
use think\Model;
use think\facade\Cache;
use think\Db;
class Wechat extends Model
{
// 微信signature签名校验
public function check() {
// 将微信服务器的请求数据分别存为变量
$signature = input('get.signature');
$timestamp = input('get.timestamp');
$nonce = input('get.nonce');
$echostr = input('get.echostr');
// 在框架配置文件中设置微信的token,并读取
$token = config('app.wechattoken');
// 将获取的数据存到一个数组中
$tmpArr = array($timestamp, $nonce, $token);
// 排序数据数组
sort($tmpArr, SORT_STRING);
// 将排序后的数组数据拼接成一个字符串
$str = implode($tmpArr);
// 判断加密后的字符串与微信请求中的signature是否一致
if(sha1($str) != $signature) {
return false;
}
return true;
}
// 获取校验access_token(区别于网页授权access_token)
public function access_token($iscache = true) {
// 如果某个参数使用较多,放到一个变量中,方便更改
$cache_key = 'access_token';
// 默认不用删除缓存
if(!$iscache) {
Cache::rm($cache_key);
}
// 获取缓存中的access_token值
$access_token = Cache::get($cache_key);
if($access_token && $iscache) {
return $access_token;
}
// 将appid和appsecret(微信公众号中获取)的值保存至config/app.php中,并调取
$appid = config('app.appid');
$appsecret = config('app.appsecret');
// 拼接url
$url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='. $appid. '&secret='. $appsecret;
// 获取access_token的值,返回一个json数据
$res = http_Get($url);
// 将json数据转换成数组
$res = json_decode($res, true);
// 如果没有拿到access_token的值,返回false
if(!isset($res['access_token'])) {
return false;
}
// 拿到数据后进行缓存,使用facade中的Cache
Cache::set($cache_key, $res['access_token'], $res['expires_in']-300);
return $res['access_token'];
}
// 换取网页授权access_token
public function auth_access_token($code) {
$appid = config('app.appid');
$appsecret = config('app.appsecret');
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='. $appid. '&secret='. $appsecret. '&code='. $code. '&grant_type=authorization_code';
$res = http_Get($url);
$res = json_decode($res, true);
if(!isset($res['access_token'])) {
return false;
}
return $res;
}
// 获取jsapi_ticket
public function jsapi_ticket($access_token, $iscache = true) {
$key = 'jsapi_ticket';
if(!$iscache) {
Cache::rm($key);
}
$data = Cache::get($key);
if($data && $iscache) {
return $data;
}
$appid = config('app.appid');
$appsecret = config('app.appsecret');
$url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='. $access_token. '&type=jsapi';
$res = http_Get($url);
$res = json_decode($res,true);
if(!isset($res['ticket'])) {
return false;
}
Cache::set($key,$res['ticket'],($res['expires_in']-300));
return $res['ticket'];
}
// 拉取用户信息
public function get_userinfo($auth_access_token, $openid) {
$url = 'https://api.weixin.qq.com/sns/userinfo?access_token='. $auth_access_token. '&openid='. $openid. '&lang=zh_CN';
$res = http_Get($url);
$res = json_decode($res, true);
return $res;
}
// 关注
public function subscribe($data) {
// 检查用户是否已存在
$user = Db::name('user')->where(array('openid' => $data['FromUserName']))->find();
// 判断是否有历史关注记录,软删除
if(!$user) {
Db::name('user')->insertGetId(array('openid' => $data['FromUserName'],'sub_status' => 1,'add_time' => time()));
} else {
Db::name('user')->where(array('openid' => $data['FromUserName']))->update(array('sub_status' => 1));
}
}
// 取消关注
public function unsubscribe($data) {
// 软删除
Db::name('user')
->where(array('openid' => $data['FromUserName']))
->update(array('sub_status' => 0));
}
// 定位
public function location($data) {
// 更新数据库中经纬度数据
Db::name('user')
->where(array('openid' => $data['FromUserName']))
->update(array('lat' => $data['Latitude'],'lng' => $data['Longitude']));
}
}
运行实例 »
点击 "运行实例" 按钮查看在线实例
地图显示视图location.html实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>微信定位</title>
</head>
<body>
</body>
<!-- 这里src中前面可不加http/https -->
<script type="text/javascript" src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script type="text/javascript">
// 此方法只有在微信中有效
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '{$appid}', // 必填,公众号的唯一标识
timestamp: {$timestamp}, // 必填,生成签名的时间戳
nonceStr: '{$nonceStr}', // 必填,生成签名的随机串
signature: '{$signature}',// 必填,签名
jsApiList: ['getLocation', 'openLocation'] // 必填,需要使用的JS接口列表
});
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
wx.ready(function(){
getLocation(openLocation);
});
// 获取地理位置接口
function getLocation(callback) {
wx.getLocation({
type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
success: function (res) {
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
// 判断返回值是否定义
if(callback != undefined) {
callback(res);
}
}
});
}
// 使用微信内置地图查看位置接口,创建一个方法
function openLocation(res) {
wx.openLocation({
latitude: res.latitude, // 纬度,浮点数,范围为90 ~ -90
longitude: res.longitude, // 经度,浮点数,范围为180 ~ -180。
name: '', // 位置名
address: '', // 地址详情说明
scale: 15, // 地图缩放级别,整形值,范围从1~28。默认为最大
infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转
});
}
</script>
</html>