正文 【微信开发:公众号】授权登录和消息模板使用方法及示例 拾年之璐 V管理员 /2020年 /319 阅读 0920 ### 微信开发之服务号:授权登录和消息模板使用示例 [TOC] ### 1. 项目简介 灵感来源于学校的 `每日健康日报`,要求使用微信小程序进行每日健康打卡。所以此项目的功能类似于 `QQ群机器人`,或者是 `每日闹钟`。 ##### 功能描述: 用户访问给定的页面,进行 `授权登录` ,然后确认自己的身份(本科生或研究生),并 `开启通知` ,即可每天在约定时间收到微信服务号的消息提醒。当然,用户可以自定义每日通知的时间,也可以随时开启或关闭每日通知。 ##### 特点对比 + 和QQ群机器人、闹钟相比,该功能的实现,**方便用户一键直达小程序**。 + 但是该方法**需关注服务号**,同时开发门槛较高。 ### 2. 项目截图  左图:用户订阅的页面,中图:用户修改通知时间的页面,右图:用户收到的消息提醒 ### 3. 开发准备 #### 3.1 开发工具 **1、编码工具**:用于项目开发。如果使用 `Java` 实现,可使用 `IntelliJ IDEA`,如果使用 `PHP` 开发,可使用 `JetBrains PhpStorm`; **2、运行工具**:用于本地测试。如果是`PHP`开发,可选择`phpstudy_pro`作为PHP项目的运行部署环境; **3、测试工具**:只能使用 `微信开发者工具` ,开发模式选择 `公众号网页调试` 。 > 微信开发者工具下载地址:[https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html) 因为涉及到微信授权登录功能,所以测试工具必须使用 `微信开发者工具`,而普通浏览器无法实现该功能。 **微信开发者工具的使用和调试方法,与普通浏览器一致**。  #### 3.2 环境支持 **1、任意域名一枚,需要开启SSL(即https访问);** **2、微信服务号一个,必须通过认证;** **3、具有外网IP的服务器一台。** > 如果仅仅是学习技术,满足前两项即可。如果项目需要上线,上述三者必不可少。 > > 对于第2条,没有服务号,也可以在`微信开放平台` 中创建网站应用。不过微信开放平台同样需要企业认证。 在上述环境均满足的情况下,接下来需要对微信服务号进行一些配置。主要有: ##### 1、获取公众号开发信息: 公众号的开发信息主要是 `开发者ID(AppID)` 和 `开发者密码(AppSecret)`。 某些接口必须使用开发者ID和开发者密码才能进行调用请求。 获取路径是:`微信公众号后台 → 开发 → 基本配置 `。 ##### 2、设置IP白名单 只有`IP白名单`中的IP地址才可以调用 `获取access_token接口`,其中access_token是进行其他操作,如发送消息模板等的必备参数。   ##### 3、配置域名 配置域名的页面访问路径是:`公众号后台 → 设置 → 公众号设置 → 功能设置`。 需要配置的域名主要有三个: **① 业务域名** > 设置业务域名后,在微信内访问该域名下页面时,不会被重新排版。用户在该域名上进行输入时,不出现安全提示。比如在微信内打开网页,输入表单的时候,不会出现“请勿输入QQ密码”等安全提示。 **② JS接口安全域名** > 设置JS接口安全域名后,公众号开发者可在该域名下调用微信开放的JS接口。比如前文中的access_token的获取,需要使用此域名。 **③ 网页授权域名** > 用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。  ##### 4、添加开发者 只有已添加的开发者,才可以使用`web开发者工具`进行对应公众号的开发和调试。 添加开发者页面的访问路径是:`微信公众号后台 → 开发 → 开发者工具 → web开发者工具`。  **5、添加模板** 在`添加功能插件`模块中,添加`模板消息`功能  然后在模板库中,搜索合适的模板,进行添加:  **其中模板ID是开发用到的参数。** ### 4. 官方参考文档解读 关于微信公众号的开发技术,及其实现路线,在官网中均有介绍。 官方文档:[https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html](https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html) 打开官方开发文档,**第一单元标题为开发前必读,开发者规范和全局返回码说明章节还是有必要读一读的**。 开发时,不能违反微信的相关规范,否则容易被封号,然后全局返回码可以帮助快速定位问题所在。  #### 4.1 网页授权登录解读 打开官方开发文档,第5章`微信网页开发`中,第2节即为`网页授权`的参考文档。 直达链接:[https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html) 该章节的目录之前,有一段关于网页授权的一些说明。 > 第一点**关于网页授权回调域名的说明**,即前文中关于域名的配置。 > > 第二点**关于网页授权的两种scope的区别说明**,主要说明的是两种授权模式,一种是静默登录授权形式,这种形式只能获取到用户的OpenID,另一种是获取用户基本信息的形式,这种形式会弹窗,需要用户手动点击登录。两种实现方式,主要是修改登录链接中的scope参数的值。后文详述。 > > 第三点**关于网页授权access_token和普通access_token的区别**,可参考文档。其实主要说明的是网页授权access_token的安全性更高,必须通过code来获取access_token,而且不同code获取的access_token不同,每一个access_token有效期2小时(7200秒),每个access_token在这2小时内,只对同一个用户有效。而普通access_token(即基础支持中的“获取access_token”接口获取到的)可以直接获取,不同场景可重复使用,有效期也为2小时,过时需要刷新。 **具体而言,网页授权流程分为四步:** 1、引导用户进入授权页面同意授权,获取code 2、通过code换取网页授权access_token(与基础支持中的access_token不同) 3、如果需要,开发者可以刷新网页授权access_token,避免过期 4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制) ##### 1. 获取code 首先需要使用户访问如下链接,才能进行登录: > https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 该链接中,**大写字母标识的字段,需要进行替换**(下同)。 上述链接中的各个参数详细说明如下: | 参数 | 是否必须 | 说明 | | :--------------- | :------- | :----------------------------------------------------------- | | appid | 是 | 公众号的唯一标识 | | redirect_uri | 是 | 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理 | | response_type | 是 | 返回类型,只能填写code | | scope | 是 | 应用授权作用域,只能为**snsapi_base** (不弹出授权页面,直接跳转,只能获取用户openid)或**snsapi_userinfo**(弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 ) | | state | 否 | 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 | | #wechat_redirect | 是 | 无论直接打开还是做页面302重定向时候,必须带此参数 | 用户确定登录后,即可在授权后重定向的回调链接地址中获取到code,格式如下: > redirect_uri/?code=CODE&state=STATE 这里的CODE是接下来换取access_token的必须参数,而STATE是开发者自定义的其他信息。 ##### 2. 换取access_token 上面获取到了code,接下来需要使用这个code来换取access_token。 请求链接如下(GET方式): > https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code 上述链接中的各个参数详细说明如下: | 参数 | 是否必须 | 说明 | | :--------- | :------- | :---------------------------- | | appid | 是 | 公众号的唯一标识、开发者ID | | secret | 是 | 公众号的appsecret、开发者密码 | | code | 是 | 填写第一步获取的code参数 | | grant_type | 是 | 只能为:authorization_code | 请求成功后,返回的JSON数据包格式如下: ```json { "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE" } ``` 关于该JSON数据包的各个字段说明如下: | 参数 | 说明 | | :------------ | :----------------------------------------------------------- | | access_token | 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 | | expires_in | access_token接口调用凭证超时时间,单位(秒) | | refresh_token | 用户刷新access_token,有效期30天。当access_token超时后,可以使用refresh_token进行刷新。 | | openid | 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID | | scope | 用户授权的作用域,使用逗号(,)分隔 | **下面是可选操作,一般用不到:** 使用`refresh_token`获取access_token的链接如下(GET方式): > https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN 上述链接中的各个参数详细说明如下: | 参数 | 是否必须 | 说明 | | :------------ | :------- | :-------------------------------------------- | | appid | 是 | 公众号的唯一标识 | | grant_type | 是 | 填写为refresh_token | | refresh_token | 是 | 填写通过access_token获取到的refresh_token参数 | 正确返回的JSON格式和上面的一致。 > **一些说明:** > > 执行到这里,我们已经拿到了用户的OpenID。如果仅仅做用户唯一性校验,至此结束即可。同样,对于发送模板消息,也只需要用户的OpenID。 ##### 3. 获取用户信息 如果网页授权作用域为 `snsapi_userinfo` ,则此时开发者可以通过 `access_token` 和 `openid` 拉取用户信息了。 请求链接如下(GET方式、**需使用https协议**): > https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN 上述链接的参数说明如下: | 参数 | 描述 | | :----------- | :----------------------------------------------------- | | access_token | 网页授权接口调用凭证,即前面步骤获取到的access_token。 | | openid | 用户的唯一标识 | | lang | 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 | 请求成功后返回的JSON数据包格式如下: ```json { "openid":" OPENID", "nickname": NICKNAME, "sex":"1", "province":"PROVINCE", "city":"CITY", "country":"COUNTRY", "headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46", "privilege":[ "PRIVILEGE1" "PRIVILEGE2" ], "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" } ``` 上述JSON数据包的参数详细说明如下: | 参数 | 描述 | | :--------- | :----------------------------------------------------------- | | openid | 用户的唯一标识 | | nickname | 用户昵称 | | sex | 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 | | province | 用户个人资料填写的省份 | | city | 普通用户个人资料填写的城市 | | country | 国家,如中国为CN | | headimgurl | 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。 | | privilege | 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom) | | unionid | 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。 | **附:检验授权凭证(access_token)是否有效** 请求链接(GET方法、需使用https协议): > https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID 上述链接的参数说明如下: | 参数 | 描述 | | :----------- | :----------------------------------------------------------- | | access_token | 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 | | openid | 用户的唯一标识 | 正确的JSON数据包返回结果如下: ```json { "errcode":0,"errmsg":"ok"} ``` 错误时的JSON返回示例: ```json { "errcode":40003,"errmsg":"invalid openid"} ``` 使用该方法可以检测access_token是否过期,以保证项目的高可用性。 #### 4.2 模板消息推送解读 打开官方开发文档,第4章 `消息管理` 中,第7节即为 `模板消息接口` 的参考文档。 直达链接:[https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html](https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html) 在官方文档中,该模块很大篇幅所叙述的内容,是针对第三方微信服务商,所以有一些是个人开发者用不到的。 总结模板消息推送的实际过程,主要流程如下: ##### 1. 获取基础access_token 请求链接如下(GET方式): > https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET 该链接中的参数详细说明如下: | 参数 | 是否必须 | 说明 | | :--------- | :------- | :------------------------------------ | | grant_type | 是 | 获取access_token填写client_credential | | appid | 是 | 第三方用户唯一凭证 | | secret | 是 | 第三方用户唯一凭证密钥,即appsecret | 请求成功返回的JSON数据包格式如下: ```json {"access_token":"ACCESS_TOKEN","expires_in":7200} ``` JSON数据包参数说明: | 参数 | 说明 | | :----------- | :--------------------- | | access_token | 获取到的凭证 | | expires_in | 凭证有效时间,单位:秒 | ##### 2. 发送消息模板 请求链接如下(POST方式): > https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN 该链接中只需要 `access_token` 一个参数,即第一步获取到的 `access_token`。 POST数据包格式为JSON格式,示例如下: ```json { "touser":"OPENID", "template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY", "url":"http://weixin.qq.com/download", "miniprogram":{ "appid":"xiaochengxuappid12345", "pagepath":"pages/index?foo=bar" }, "data":{ "first": { "value":"恭喜你购买成功!", "color":"#173177" }, "keyword1":{ "value":"巧克力", "color":"#173177" }, "keyword2": { "value":"39.8元", "color":"#173177" }, "keyword3": { "value":"2014年9月22日", "color":"#173177" }, "remark":{ "value":"欢迎再次购买!", "color":"#173177" } } } ``` 该JSON数据包中的各个参数详细说明如下: | 参数 | 是否必填 | 说明 | | :---------- | :------- | :----------------------------------------------------------- | | touser | 是 | 接收者openid | | template_id | 是 | 模板ID,前文配置中添加模板消息后获得的一串字符 | | url | 否 | 模板跳转链接(海外帐号没有跳转能力) | | miniprogram | 否 | 跳小程序所需数据,不需跳小程序可不用传该数据。当链接和小程序都存在时,优先跳转小程序。 | | appid | 是 | 所需跳转到的小程序appid(该小程序appid必须与发模板消息的公众号是绑定关联关系,暂不支持小游戏) | | pagepath | 否 | 所需跳转到小程序的具体页面路径,支持带参数,(示例pages/index?foo=bar),要求该小程序已发布,暂不支持小游戏 | | data | 是 | 模板数据。这里的数据个数,需要与消息模板中的参数对应,如下图所示。 | | color | 否 | 模板内容字体颜色,不填默认为黑色 |  请求成功后,返回的JSON数据包格式如下所示: ```json { "errcode":0, "errmsg":"ok", "msgid":200228332 } ``` 这时,用户手机微信将收到对应的提醒。 行文至此,讲述的主要是授权登录和发送模板消息的大致实现流程。 下文,将结合实际的项目,对上述功能的实现流程,以项目源码的形式,进行更加详细的演示。 ### 5. 网页授权登录实现(PHP为例) #### 5.1 工具类GetWX 该工具类的主要功能是根据微信授权登录的实现流程,进行链接请求和数据获取。 ```php appid; $scope = 'snsapi_userinfo'; $state = md5(uniqid(rand(), TRUE));//唯一ID标识符绝对不会重复 $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $appid . '&redirect_uri=' . urlencode($callback) . '&response_type=code&scope=' . $scope . '&state=' . $state . '#wechat_redirect'; header("Location:$url"); } /** * 功能:通过code获取access_token * * @param $code * @return mixed */ public function get_access_token($code) { $appid = $this->appid; $appsecret = $this->appsecret; $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appid . '&secret=' . $appsecret . '&code=' . $code . '&grant_type=authorization_code'; $data = json_decode(file_get_contents($url));//返回的json数组转换成array数组 return $data; } /** * 功能:使用access_token获取用户信息 * * @param $access_token * @param $openid * @return mixed */ public function get_user_info($access_token, $openid) { $url = 'https://api.weixin.qq.com/sns/userinfo?access_token=' . $access_token . '&openid=' . $openid . '&lang=zh_CN'; $data = json_decode(file_get_contents($url));//返回的json数组转换成array数组 return $data; } } ?> ``` 关于该工具类的具体实现方法不唯一。后文中有第二种完整的示例。 #### 5.2 获取code 登录回调页面(login.php)的源码如下: ```php get_code($callback); } //GET到了code,可以执行后续操作 else { } ``` 如上源码所示,这时访问https://XXXX.cn/test/login.php,执行到if语句处,判断链接中没有code这个参数,所以获取了当前链接的URL地址(callback参数),并将其交给get_code方法,该方法将构造登录链接,并前往该页面,然后弹出登录授权页面,如下所示:  用户点击 `同意` 之后,返回到$callback链接。这时,链接中携带code参数,以及开发者自定义的state参数。如下图所示:  我们暂时用到的是code参数。 #### 5.3 换取access_token 接下来,使用该 `code换取access_token`,继续完善login.php代码,具体如下: ```php get_code($callback); } //GET到了code,可以执行后续操作 else { //从链接中拿code $code = $_GET['code']; //获取网页授权access_token和用户openid $data = $getWX->get_access_token($code); //测试输出 echo 'access_token:' . $data->access_token . ''; echo 'expires_in:' . $data->expires_in . ''; echo 'refresh_token:' . $data->refresh_token . ''; echo 'openid:' . $data->openid . ''; echo 'scope:' . $data->scope . ''; } ``` 输出的信息如下:  #### 5.4 获取用户信息 上面拿到了 `access_token` 和 `OpenID`,接下来可以使用这两个参数来获取用户的基本信息。 继续完善login.php,如下: ```php get_code($callback); } //GET到了code,可以执行后续操作 else { //从链接中拿code $code = $_GET['code']; //获取网页授权access_token和用户openid $data = $getWX->get_access_token($code); //获取微信用户信息 $userInfo = $getWX->get_user_info($data->access_token, $data->openid); //测试输出: echo 'openid:' . $userInfo->openid . ''; echo 'nickname:' . $userInfo->nickname . ''; echo 'sex:' . $userInfo->sex . ''; echo 'province:' . $userInfo->province . ''; echo 'city:' . $userInfo->city . ''; echo 'country:' . $userInfo->country . ''; echo 'headimgurl:' . $userInfo->headimgurl . ''; } ``` 执行结果如下图所示:  这时,我们就拿到了用户的基本信息,之后就可以将这些基本信息保存到数据库,用户ID等可以保存到session或者cookie中,等等,进行各种其他操作。 #### 5.5 另一种完整的示例 工具类: ```php get_code($callback); } else {//获取code后跳转回来到这里了 $code = $_GET['code']; $data = $this->get_access_token($code);//获取网页授权access_token和用户openid $userInfo = $this->get_user_info($data['access_token'], $data['openid']);//获取微信用户信息 return $userInfo; } } /** * 2、用户授权并获取code * @param string $callback 微信服务器回调链接url */ public function get_code($callback) { $appid = $this->appid; $scope = 'snsapi_userinfo'; $state = md5(uniqid(rand(), TRUE));//唯一ID标识符绝对不会重复 $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $appid . '&redirect_uri=' . urlencode($callback) . '&response_type=code&scope=' . $scope . '&state=' . $state . '#wechat_redirect'; header("Location:$url"); } /** * 3、使用code换取access_token * @param string 用于换取access_token的code,微信提供 * @return array access_token和用户openid数组 */ public function get_access_token($code) { $appid = $this->appid; $appsecret = $this->appsecret; $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appid . '&secret=' . $appsecret . '&code=' . $code . '&grant_type=authorization_code'; $user = json_decode(file_get_contents($url)); if (isset($user->errcode)) { echo 'error:' . $user->errcode . 'msg :' . $user->errmsg; exit; } $data = json_decode(json_encode($user), true);//返回的json数组转换成array数组 return $data; } /** * 4、使用access_token获取用户信息 * @param string access_token * @param string 用户的openid * @return array 用户信息数组 */ public function get_user_info($access_token, $openid) { $url = 'https://api.weixin.qq.com/sns/userinfo?access_token=' . $access_token . '&openid=' . $openid . '&lang=zh_CN'; $user = json_decode(file_get_contents($url)); if (isset($user->errcode)) { echo 'error:' . $user->errcode . 'msg :' . $user->errmsg; exit; } $data = json_decode(json_encode($user), true);//返回的json数组转换成array数组 return $data; } } ?> ``` 登录页login.php: ```php get_code($callback); } //获取code后跳转回来到这里了 else { $code = $_GET['code']; //获取网页授权access_token和用户openid $data = $getWxUser->get_access_token($code); //获取微信用户信息(数组) $userInfo = $getWxUser->get_user_info($data['access_token'], $data['openid']); //保存到数据库 if ($userInfo['openid'] != '') { saveUserInfo($userInfo['openid'], $userInfo['nickname'], $userInfo['sex'], $userInfo['province'], $userInfo['city'], $userInfo['country'], $userInfo['headimgurl']); } //保存到cookie里 setcookie('openid', $userInfo['openid'], time() + 3600 * 24 * 30, "/"); //返回主页 header("Location:" . $homeUrl); } ?> ``` ### 6. 模板消息推送实现(PHP为例) #### 6.1 官方JSSDK文档(PHP) 该SDK实现了各种参数的获取,源码如下: ```php appId = $appId; $this->appSecret = $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_php_file("jsapi_ticket.php")); if ($data->expire_time < time()) { $accessToken = $this->getAccessToken(); // 如果是企业号用以下 URL 获取 ticket // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=$accessToken"; $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken"; $res = json_decode($this->httpGet($url)); $ticket = $res->ticket; if ($ticket) { $data->expire_time = time() + 7000; $data->jsapi_ticket = $ticket; $this->set_php_file("jsapi_ticket.php", json_encode($data)); } } else { $ticket = $data->jsapi_ticket; } return $ticket; } public function getAccessToken() { // access_token 应该全局存储与更新,以下代码以写入到文件中做示例 $data = json_decode($this->get_php_file("access_token.php")); if ($data->expire_time < time()) { // 如果是企业号用以下URL获取access_token // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret"; $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret"; $res = json_decode($this->httpGet($url)); $access_token = $res->access_token; if ($access_token) { $data->expire_time = time() + 7000; $data->access_token = $access_token; $this->set_php_file("access_token.php", json_encode($data)); } } else { $access_token = $data->access_token; } return $access_token; } private function httpGet($url) { $curl = curl_init(); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_TIMEOUT, 500); // 为保证第三方服务器与微信服务器之间数据传输的安全性,所有微信接口采用https方式调用,必须使用下面2行代码打开ssl安全校验。 // 如果在部署过程中代码在此处验证失败,请到 http://curl.haxx.se/ca/cacert.pem 下载新的证书判别文件。 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true); curl_setopt($curl, CURLOPT_URL, $url); $res = curl_exec($curl); curl_close($curl); return $res; } private function get_php_file($filename) { return trim(substr(file_get_contents($filename), 15)); } private function set_php_file($filename, $content) { $fp = fopen($filename, "w"); fwrite($fp, "" . $content); fclose($fp); } } ``` #### 6.2 模板消息JSON化 这里以下面这个模板消息为实例,进行演示。模板 `详细内容` 处的内容需要牢记,因为在构造JSON时,要用这些信息。  然后定义一个函数,主要功能是实现发送数据的JSON格式化 函数源码: ```php //模板消息JSON格式化 function json_tempalte($openid, $appid, $nickname, $time) { //模板消息 $template = array( 'touser' => $openid, //用户openid 'template_id' => 'XXXX', //在公众号下配置的模板id 'url' => 'https://XXXX.cn', //点击模板消息会跳转的链接 'miniprogram' => array( 'appid' => $appid, 'pagepath' => 'pages/main/login/login?foo=bar' ), 'data' => array( 'first' => array('value' => '完成今日健康日报了吗?未完成,请尽快哦~', 'color' => "#FF0000"), 'keyword1' => array('value' => $nickname, 'color' => '#173177'), //keyword需要与配置的模板消息对应 'keyword2' => array('value' => $time, 'color' => '#173177'), 'remark' => array('value' => '点击此处前往填报。本通知由您订阅产生,切换身份、更改通知时间及退订管理,请访问菜单栏『学习助手』模块。', 'color' => '#FF0000'), ) ); $json_template = json_encode($template); return $json_template; } ``` 这个函数返回的结果是JSON数据包。 #### 6.3 发送模板消息 关键源码如下: ```php include("jssdk.php"); $jssdk = new JSSDK("APPID", "APPSECRET"); //定义消息模板JSON $json_template = json_tempalte('APPID', 'wxa0738e54aae84423', '拾年之璐' . '(本科生)', date("Y-m-d")); //获取AccessToken $accessToken = $jssdk->getAccessToken(); //POST 的链接 $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" . $accessToken; //提交 list($returnCode, $returnContent) = http_post_json($url, $json_template); //打印日志(JSON格式) echo '发送结果如下:' . $returnContent . '\n'; ``` 其中,上面代码中的 `http_post_json` 函数如下: ```php //发送POST请求 function http_post_json($url, $jsonStr) { $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json; charset=utf-8', 'Content-Length: ' . strlen($jsonStr) ) ); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return array($httpCode, $response); } ``` 该函数是自己封装的进行POST请求。当然各种 执行完成后,用户即可收到消息模板。 ### 7. 后记 行文至此,关于微信授权登录和微信模板消息推送的主要使用方法讲解完毕。 前文中虽然有提到过每日提醒的模板消息推送功能,但这里将不再论述,同时相关源码不开源。因为此功能处于微信模板消息使用规则的灰色地带。笔者也曾和微信公众号团队联系过,对方纳入了产品评估。 本文采用创作共用版权 CC BY-NC-SA 3.0 CN 许可协议,转载或复制请注明出处! -- 展开阅读全文 --