EasyWeChat4.x开发微信第三方公众平台踩坑记录

流氓凡 技术分享 2022-05-08 4.53 K 0

背景

利用微信第三方开放平台接口,使其公众号管理员自动授权并且获取其授权公众号的所有接口权限,比如客服消息、用户信息、模板消息等等。

需要注意的是,本篇文章代码全部是伪代码,用于理解部分文字说明,后续请结合自己实际业务自行编写实现

遇到的问题

1. 第三方平台推送接收不到或者存储不了Ticket

2. 临时素材上传问题

3. 无法获取用户更多资料

4. authorizer_access_token经常失效无法保存

5. 授权回调地址打开提示域名相关问题

6. 小程序客服消息回复问题

相关链接

EasyWeChat文档:https://easywechat.com/4.x/open-platform/index.html

微信官方文档:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Before_Develop/Authorization_Process_Technical_Description.html

微信第三方公众平台:https://open.weixin.qq.com/cgi-bin/index?t=home/index&lang=zh_CN

授权流程简单分析

首先我们先了解下整体授权流程,这个官方文档有详细说明,这里简单说下:

我们先定义一个接口用于接收授权事件接收配置,也就是说这个接口用于接收微信平台每10分钟推送一次的 component_verify_ticket 值 这个值sdk会自动缓存12小时,后面每个接口都需要这个值参与当然sdk已经默认处理好了

我们需要做的就是定义这个接口,然后等待微信平台给这个接口推送数据,至于这个接口写什么后面会讲到。


然后我们开始拼接拉起授权的url地址,这个地址的作用就是公众号管理员点击这个地址开始进入授权,点击授权就到了回调地址页面并传递数据


最后定义一个回调接口用于记录授权的平台相关信息,同时注意存储authorizer_refresh_token值

介绍下相关的token值

1. component_verify_ticket 这个是微信官方每间隔10分钟向我们接口推送过来的,接口配置在image.png

    作用是用来获取令牌(component_access_token)

2. authorizer_refresh_token 这个是刷新令牌(component_access_token),文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/api_authorizer_token.html 这个值也会在授权成功的时候返回在回调地址的接口中,应该直接存储在数据库中,后面所有代授权接口调用都需要此值,这个值是永久存储的可以理解为类似微信小程序中的AppSecret的概念

3. component_access_token 也叫令牌,文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/component_access_token.html 这个值是每个接口必传参数,类似微信小程序中的access_token,上面两个的值都是为了稳定的获取它才出现的,有效期2小时这个sdk已经默认做了处理,但是有个坑后面会讲到。

4. pre_auth_code 这个是代公众号拉起用户授权返回的,跟正常公众号拉起授权一样使用就行。

4个推送事件

也就是说只有这四个事件会触发授权事件接收配置的接口!

授权成功事件(authorized)、授权更新事件(updateauthorized)、授权取消事件(unauthorized)、Ticket事件(component_verify_ticket)

进入开发

上面所有准备工作和需要了解的知识有普及完毕,下面开始正式接入流程了

首先我们按照授权的流程先来写接口,第一个接口是定义一个授权事件接收配置

路由的定义:

Route::post('openPlatformNotify', '\App\Http\Controllers\Api\OpenPlatformController@notify');

整体类代码如下,基本所有代码都在这里类里面实现,因此后面在继续补全代码不会再重复定义一些代码了:

<?php
namespace App\Http\Services;

use EasyWeChat\Factory;
use EasyWeChat\Kernel\Messages\Text;
use EasyWeChat\OpenPlatform\Application;
use EasyWeChat\Kernel\Messages\Image;

class OpenPlatformService
{
    /**
     * 第三方平台配置信息
     * @var array
     */
    public $config;
    
    public function __construct()
    {
        $this->config = [
            'app_id' => '第三方平台的应用appid',
            'secret' => '第三方平台应用appsecret',
            'token' => '第三方平台应用消息校验Token',
            'aes_key' => '第三方平台应用消息加解密Key',
            'http' => [
                'throw' => true, // 状态码非 200、300 时是否抛出异常,默认为开启
                'timeout' => 5.0,
                'retry' => true, // 使用默认重试配置
            ],
        ];
        // 实例化业务
        $this->openPlatform = Factory::openPlatform($this->config);
    }
    
    /**
     * 第三方推送事件处理
     * @return \Symfony\Component\HttpFoundation\Response
     * @throws \EasyWeChat\Kernel\Exceptions\BadRequestException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \ReflectionException
     */
    public function notify()
    {
        $server = $this->openPlatform->server;
        // 定义这个主要是实现公众号后台点击取消第三方平台会接收通知,来处理自己数据库存储的业务逻辑比如更改已授权的平台记录状态字段等
        $server->push(function ($message) {
            // 这里实现更改已授权平台记录业务逻辑
        }, Guard::EVENT_UNAUTHORIZED);
        
        return $server->serve();
    }
    
}

OK,这代码已经完事了,然后就是在微信第三方平台中配置下

image.png

就等10分钟后的微信官方推送就OK了。其他的处理sdk已经都做好了,那为了不浪费时间在这时候可以在编写获取拉起授权的地址


拉起授权地址用于将该链接发送给想要授权的公众号管理员,让他点击会出现授权提示类似下面这种

image.png

还是先定义个路由:

// 第三方平台授权回调地址
Route::any('callbackUrl', '\App\Http\Controllers\Api\OpenPlatformController@callbackUrl');

然后看下callbackUrl函数代码:

    /**
     * 授权回调处理
     * @param $data 来自$request->all()
     * @return bool
     */
    public function callbackUrl($data)
    {
        // 这个结果是包含了authorizer_refresh_token和component_access_token参数
        $result = $this->openPlatform->handleAuthorize($data['auth_code']);
        info($result);
        
        // 重点坑来了,这里必须重设token防止失效,不然重新更新授权之前的authorizer_access_token不会随之更新,那依旧使用的话就提示失效了
        // 注意这个$platformInfo['authorizer_refresh_token']参数,这个是上一步$result得到的并且永久存储在数据库的,这里不能直接使用$result中返回的最新authorizer_refresh_token值
        // 否则个别情况会出现authorizer_refresh_token失效,(公众号更改名称或者微信号,再次重新授权)
        $officialAccount = $this->openPlatform->officialAccount($result['authorization_info']['authorizer_appid'], $platformInfo['authorizer_refresh_token']);
        $officialAccount->access_token->setToken($result['authorization_info']['authorizer_access_token']);

        $info = $this->openPlatform->getAuthorizer($result['authorization_info']['authorizer_appid']);
        $authorizer_info = $info['authorizer_info'];
        // 回调返回的公众号授权详细信息,大致包括名,头像,二维码,公众号类型等等
        info($message);
        return true;
    }

好了,回调地址我们接下来就直接定义获取授权地址,并将这个回调地址填写到参数中:

路由:

Route::post('getPreAuthorizationUrl', '\App\Http\Controllers\Api\OpenPlatformController@getPreAuthorizationUrl');

函数代码如下:

    /**
     * 获取用户授权页 URL
     * @param $data 来自$request->all()
     * @return array
     */
    public function getPreAuthorizationUrl($data)
    {
        // 授权成功回调地址,可以在后面拼接参数传递也没关系的
        $callback_url = getenv('APP_URL') . '/api/v1/callbackUrl';
        $result = $this->openPlatform->getPreAuthorizationUrl($callback_url); // pc旧版授权页url
        // 生成h5新版授权页url
        $urlData = parse_url(urldecode($result));
        preg_match('/pre_auth_code=(.*?)&/', $urlData['query'], $arr);
        $pre_auth_code = $arr[1];
        // 太长了 自行拼接转换下
        $url = '
        https://open.weixin.qq.com/wxaopen/safe/bindcomponent?action=bindcomponent&no_scan=1
        &component_appid=' . $this->config['app_id'] . '&pre_auth_code=' . $pre_auth_code . '&redirect_uri=' . $callback_url . '
        &auth_type=1#wechat_redirect';
        return [
            'h5_url' => $url,
            'pc_url' => $result,
        ];
    }

URL授权地址详细介绍看文档:

image.png

使用场景介绍:

image.png

Url中的参数说明:

image.png

这里需要注意的是,如果你使用PC版url,那必须嵌套在a标签中,利用a标签的href进行跳转,否则会提示域名错误,错误具体提示忘记了,可以自己试下


到了这里教程已经过半了,下面我们来配置些无关紧要的东西吧,比如客服消息或者公众号或小程序的接收平台推送的消息与事件等等。

定义路由:

// 第三方平台消息接收事件处理
Route::any('openMessage/{appid}/callback', '\App\Http\Controllers\Api\OpenPlatformController@openMessage');

然后我们看下控制器函数方法代码,

/**
     * 消息与事件接收配置接收
     * @param Request $request
     */
    public function openMessage(Request $request)
    {
        $appid = $request->appid;
        return (new openPlatformService())->openMessage($appid);
    }

Servicec层openMessage方法代码:

    /**
     * 消息与事件接收配置(微信公众号或小程序)
     * @param $appid
     * @return false|\Symfony\Component\HttpFoundation\Response
     * @throws \EasyWeChat\Kernel\Exceptions\BadRequestException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \ReflectionException
     */
    public function openMessage($appid)
    {
        // 代公众号实现业务
        $officialAccount = $this->openPlatform->officialAccount($appid);
        $server = $officialAccount->server;
        $server->push(function ($message) use ($officialAccount) {
            info($message);
            if ($message['MsgType'] == 'event' && $message['Event'] == 'subscribe') {
                // 关注事件
                return '感谢您的关注';
            }
            if ($message['MsgType'] == 'event' && $message['Event'] == 'unsubscribe') {
                // 取消关注事件
                return $this->subscribeEvent($message);
            }
            if ($message['MsgType'] == 'text') {
                // 其他文本类消息
            }
        });
        return $server->serve();
    }

然后将地址填写在这里,注意token和key必须与$this-config中填写的对应上:

image.png


最后一个坑,小程序客服消息发现不是这里的,那就没什么好说的了,基本就这些。


评论