1. 开通微信公众号:订阅号
到微信公众号开通个人订阅号,并查看相关开发文档,并了解消息管理中的被动回复用户信息。
2. 实现对接微信公众号功能
对接代码
signature = $_GET["signature"];timestamp = GET["timestamp"];_GET["timestamp"];GET["timestamp"];nonce = GET["nonce"];_GET["nonce"];GET["nonce"];echostr = $_GET["echostr"];
token=′leeprinceSubscription′;token = 'leeprinceSubscription';token=′leeprinceSubscription′;tmpArr = array(token, $timestamp, $nonce); sort(tmpArr, SORT_STRING);
tmpStr=implode(tmpStr = implode(tmpStr=implode(tmpArr);
tmpStr=sha1(tmpStr = sha1(tmpStr=sha1(tmpStr);
if (tmpStr == $signature) { // 微信服务器在验证业务服务器地址的有效性时,包含 echostr 参数,之后微信服务器与业务服务器的交互不再包含此参数。 if (empty(echostr)) {
/**
* 开始处理业务
*/
return true;
} else {
// 若确认此次GET请求来自微信服务器,请原样返回echostr参数内容. 此处只能使用 echo,使用 return 失败。
echo $echostr;
}
} else {
return false;
}
在 「微信公众号->开发->基本配置->服务器配置」 中配置信息,其中服务器地址(URL)要能被外网访问,第一次配置会传入 echostr 参数进行服务器地址的有效校验,之后微信服务器与业务服务器的交互不再包含此参数。可以正确提交即检验成功,之后启动该服务配置。后面可以在「管理->消息管理」中查看用户给公众号发的消息。
补充:基于内网开发的可以使用 ngrok 进行内网穿透。ngrok 会分配 http 和 https
的临时链接供你使用的。(mac使用:/Applications/ngrok http 80)其中 「Web Interface」的本地链接
http://127.0.0.1:4040 是 web 页面用于查看 ngrok 转发的信息
ngrok by @inconshreveable (Ctrl+C to quit)
Session Status online
Session Expires 7 hours, 59 minutes
Version 2.3.35
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://6a33d7ab.ngrok.io -> http://localhost:80
Forwarding https://6a33d7ab.ngrok.io -> http://localhost:80
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
3. 构建微信公众号对接功能到 composer 扩展包中
在项目(我的本地项目路径是:xxx/www/composer/laravel-wechat 此处为了更好在下文说明)中执行 composer init。本地没有 composer 的自行去官网下载吧。
> composer init
Package name (<vendor>/<name>) [leeprince/laravel-wechat]:
Description []: This is laravel WeChat composer
Author [leeprince <leeprince@foxmail.com>, n to skip]:
Minimum Stability []:
Package Type (e.g. library, project, metapackage, composer-plugin) []: library
License []: MIT
Define your dependencies.
Would you like to define your dependencies (require) interactively [yes]?
Search for a package:
Would you like to define your dev dependencies (require-dev) interactively [yes]?
Search for a package:
{
"name": "leeprince/laravel-wechat",
"description": "This is laravel WeChat composer",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "leeprince",
"email": "leeprince@foxmail.com"
}
],
"require": {}
}
Do you confirm generation [yes]?
Would you like the vendor directory added to your .gitignore [yes]?
在 composer.json 文件中添加自动加载配置
"autoload": {
"psr-4": {
"LeePrince\\WeChat\\": "./src/"
}
}
执行 composer update 更新获取依赖的最新版本
4. laravel 项目集成本地基于微信公众号开发 composer 组件包
在已经下载好的 laravel 项目(我的本地路径是:xxx/www/laravel69)中配置 composer laravel-wechat 扩展的本地仓库的相对路径或者绝对路径
composer config repositories.leeprince path ../composer/laravel-wechat
增加新的依赖包到当前项目的 ./vendor/ 中
composer require leeprince/laravel-wechat:dev-master
5. 编写服务提供者,并注册到 laravel 的服务提供者中
这是将该 composer leeprince/laravel-wechat 扩展包集成到 laravel 的第一步
在 laravel-wechat 项目的 ./src 路径下 编写服务提供者 WeChatServiceProvider 并继承 laravel 的服务提供者。注意命名空间为:namespace LeePrince\WeChat; 该服务提供者用于加载自定义组件中的所有服务
<?php
/**
* [服务提供者为 laravel 提供该组件的所有服务]
*
* @Author leeprince:2020-03-22 19:05
*/
namespace LeePrince\WeChat;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;
class WeChatServiceProvider extends ServiceProvider
{
...
}
在 laravel 项目的 ./config/app.php 中注册该 composer 扩展包的服务提供者
/**
* 引入 composer 组件中的服务提供者
*/
// 自定义服务提供者 - 基于微信公众号开发的 composer 组件
LeePrince\WeChat\WeChatServiceProvider::class,
6. 【核心服务:路由】
在 ./src/Route/route.php 中编写路由
Router::any('hello', function() {
dump('hello world');
});
在服务提供者 WeChatServiceProvider boot() 方法中加载路由
/**
* [引导服务:该方法在所有服务提供者被注册以后才会被调用]
*
* @Author leeprince:2020-03-22 19:55
*/
public function boot()
{
$this->loadRoutes();
}
/**
* [加载路由]
*
* @Author leeprince:2020-03-23 00:28
*/
private function loadRoutes()
{
Route::group(['namespace' => 'LeePrince\WeChat\Http\Controllers', 'prefix' => 'wechat'], function() {
$this->loadRoutesFrom(__DIR__.'/Route/route.php');
});
}
7. 【核心服务:控制器】
在 ./src/Http/Controllers/WxSubscriptionController.php.php 中编写控制器。由于上一步已经加载了路由,所以编写的控制器可以立即生效。
<?php
namespace LeePrince\WeChat\Http\Controllers;
use Illuminate\Http\Request;
/**
* [微信公众号 - 订阅号接受微信服务验证、接受微信从订阅号发送过来的信息并自动回复]
*
* @Author leeprince:2020-03-22 20:06
* @package LeePrince\WeChat\Http\Controllers
*/
class WxSubscriptionController extends Controller
{
/**
* [自动回复微信公众号信息]
*
* @Author leeprince:2020-03-24 01:10
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function index(Request $request)
{
/**
* 开始处理业务
*/
$signature = $request->input("signature");
$timestamp = $request->input("timestamp");
$nonce = $request->input("nonce");
$echostr = $request->input("echostr");
$token = 'leeprinceSubscription';
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr, SORT_STRING);
$tmpStr = implode($tmpArr);
$tmpStr = sha1($tmpStr);
if ($tmpStr == $signature) {
// 微信服务器在验证业务服务器地址的有效性时,包含 echostr 参数,之后微信服务器与业务服务器的交互不再包含此参数。
if (empty($echostr)) {
/**
* 开始处理业务
*/
// 回复信息
// 接收微信发送的参数
$postObj =file_get_contents('php://input');
$postArr = simplexml_load_string($postObj,"SimpleXMLElement",LIBXML_NOCDATA);
if (empty($postArr)) {
return response('XML消息为空!');
}
//消息内容
$content = $postArr->Content;
//接受者
$toUserName = $postArr->ToUserName;
//发送者
$fromUserName = $postArr->FromUserName;
//获取时间戳
$time = time();
//回复消息内容; 补充:想更加只能可以通过接入机器人自动回复。比如图灵机器人:http://www.tuling123.com
$content = "[PrinceProgramming] - 编程是一门艺术\n欢迎您,您的消息是: $content\n";
//回复文本消息的格式:把百分号(%)符号替换成一个作为参数进行传递的变量
$info = sprintf("<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>", $fromUserName, $toUserName, $time, $content);
return response($info);
} else {
// 若确认此次GET请求来自微信服务器,请原样返回echostr参数内容. 此处只能使用 echo,使用 return 失败。
echo $echostr;
}
} else {
return response('false');
}
}
}
8.【扩展服务:中间件】
测试通过后继续完善代码提取检测签名部分到中间件中作为请求过滤,这里是用的是路由中间件。需要在 WeChatServiceProvider 服务提供者中加载路由中间件到路由中。
<?php
/**
* [服务提供者为 laravel 提供该组件的所有服务]
*
* @Author leeprince:2020-03-22 19:05
*/
namespace LeePrince\WeChat;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;
class WeChatServiceProvider extends ServiceProvider
{
// 中间件组
private $middlewareGroups = [];
// 路由中间件
private $routeMiddleware = [
'wechat.subscription.CheckSignture' => \LeePrince\WeChat\Http\Middleware\CheckSignture::class,
];
/**
* [注册服务:register 方法中,你只需要将服务绑定到服务容器中。而不要尝试在 register 方法中注册任何监听器,路由,或者其他任何功能。否则,你可能会意外地使用到尚未加载的服务提供者提供的服务。]
*
* @Author leeprince:2020-03-22 19:56
*/
public function register()
{
}
/**
* [引导服务:该方法在所有服务提供者被注册以后才会被调用]
*
* @Author leeprince:2020-03-22 19:55
*/
public function boot()
{
$this->syncMiddlewareToRouter();
}
/**
* 将中间件的当前状态同步到路由器
*
* @return void
*/
private function syncMiddlewareToRouter()
{
foreach ($this->middlewareGroups as $key => $middleware) {
Route::middlewareGroup($key, $middleware);
// $this->router->middlewareGroup($key, $middleware);
}
foreach ($this->routeMiddleware as $key => $middleware) {
Route::aliasMiddleware($key, $middleware);
}
}
}
9.【扩展服务:视图】
在 ./src/Resources/views/view.blade.php 中编写视图文件。
在 ./src/Resources/ 下的目录结构为
Resources
js
lang
sass
views
view.blade.php 文件
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
</head>
<body>
<div class="content">
LeePrince
<br>
[PrinceProgramming] - 编程是一种艺术
<hr>
</div>
</body>
</html>
在 WeChatServiceProvider 服务提供者中加载视图文件并设置命名空间
<?php
/**
* [服务提供者为 laravel 提供该组件的所有服务]
*
* @Author leeprince:2020-03-22 19:05
*/
namespace LeePrince\WeChat;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;
class WeChatServiceProvider extends ServiceProvider
{
/**
* [注册服务:register 方法中,你只需要将服务绑定到服务容器中。而不要尝试在 register 方法中注册任何监听器,路由,或者其他任何功能。否则,你可能会意外地使用到尚未加载的服务提供者提供的服务。]
*
* @Author leeprince:2020-03-22 19:56
*/
public function register()
{
}
/**
* [引导服务:该方法在所有服务提供者被注册以后才会被调用]
*
* @Author leeprince:2020-03-22 19:55
*/
public function boot()
{
$this->loadViews();
}
/**
* [加载视图]
*
* @Author leeprince:2020-03-24 01:08
*/
private function loadViews()
{
$this->loadViewsFrom(__DIR__."/Resources/views/", 'wechatview');<
