技术饭

微信开放平台中第三方平台api接口的对接与实现

copylian    0 评论    13660 浏览    2020.08.28

微信开放平台中第三方平台api接口的对接与实现,最近为了做微信开放平台的第三方平台也是花费了两周多的时间,中间碰到各种坑,以前如果要帮助第三方公众号做开放,也是直接把第三方的开发信息改成自己服务器的,但是这样不方便,不同服务商每做一个活动都要重新改一次,所以只能使用公众号授权第三方平台来实现这样的功能了。

一、申请微信第三方平台微信开放平台中第三方平台账号申请与配置

二、微信第三方平台处理基类:主要对接微信第三方平台接口,获取相应信息

<?php
/**
 * Author: [ CopyLian ]
 * Date: [ 2017.02.23 ]
 * Email: [ copylian@aikehou.com ]
 * Site: [ http://www.copylian.com ]
 * Description [ 微信第三方平台处理类 ]
 */
namespace wechat;

class Thirdplatform {

    //前缀
    private $prefix = 'test_';

    //接口域名
    private $url = 'https://api.weixin.qq.com/cgi-bin/';

    //授权地址
    private $auth_url = 'https://mp.weixin.qq.com/cgi-bin/';

    //获取code地址
    private $requestCodeURL = 'https://open.weixin.qq.com/connect/oauth2/authorize';

    //权限api的地址
    private $oauthApiURL = 'https://api.weixin.qq.com/sns';

    //APPID
    private $appid;

    //APPSECRET
    private $appsecret;

    //消息校验Token
    private $token;

    //消息加解密Key
    private $key;

    /**
     * [__construct 构造函数]
     */
    public function __construct($prefix = '') {

        //前缀
        $this->prefix = !empty($prefix) ? $prefix : $this->prefix;

        //初始化数据
        $this->appid     = config('thirdplatform.' . $this->prefix . 'thirdplatform_account.appid');
        $this->appsecret = config('thirdplatform.' . $this->prefix . 'thirdplatform_account.appsecret');
        $this->token     = config('thirdplatform.' . $this->prefix . 'thirdplatform_account.token');
        $this->key       = config('thirdplatform.' . $this->prefix . 'thirdplatform_account.key');
    }

    /**
    * [get_thirdplatform_auth 授权注册页面扫码授权:注意此URL必须放置在页面当中用户点击进行跳转,不能通过程序跳转,否则将出现“请确认授权入口页所在域名,与授权后回调页所在域名相同....”错误]
    * @param  [type]  $redirect_uri [扫码成功后的回调地址]
    * @param  integer $auth_type    [要授权的帐号类型,1-则商户扫码后,手机端仅展示公众号;2-表示仅展示小程序;3-表示公众号和小程序都展示。如果为未指定,则默认小程序和公众号都展示。第三方平台开发者可以使用本字段来控制授权的帐号类型。]
    * @return [type]                [description]
    */
    public function get_thirdplatform_auth($redirect_uri = '', $auth_type = 3){

        //请求地址
        $url = $this->auth_url . "componentloginpage?component_appid=" . $this->appid . "&pre_auth_code=" . $this->get_pre_auth_code() . "&redirect_uri=" . urlencode($redirect_uri) . "&auth_type=" . $auth_type;
        return $url;
    }

    /**
     * [get_query_auth 使用授权码获取授权信息]
     * @param  string $auth_code [授权码, 会在授权成功时返回给第三方平台,详见第三方平台授权流程说明]
     */
    public function get_query_auth($auth_code = ''){

        //请求地址
        $url = $this->url . "component/api_query_auth?component_access_token=" . $this->get_component_access_token();

        //发送数据
        $data = [
            "component_appid"         => $this->appid,
            "authorization_code"      => $auth_code
        ]; 

        //请求并返回数据
        $res = $this->https_post($url, json_encode($data));
        $res = object_to_array(json_decode($res));
        if(isset($res['errcode']) && $res['errcode'] != 0) {
            //报错,返回空
            return '';
        } else {
            return $res['authorization_info'];
        }
    }

    /**
     * [get_authorizer_info 获取授权方的帐号基本信息]
     * @param  string $auth_appid [授权方 appid]
     */
    public function get_authorizer_info($auth_appid = ''){

        //请求地址
        $url = $this->url . "component/api_get_authorizer_info?component_access_token=" . $this->get_component_access_token();

        //发送数据
        $data = [
            "component_appid"         => $this->appid,
            "authorizer_appid"        => $auth_appid
        ]; 

        //请求并返回数据
        $res = $this->https_post($url, json_encode($data));
        $res = object_to_array(json_decode($res));
        if(isset($res['errcode']) && $res['errcode'] != 0) {
            //报错,返回空
            return [];
        } else {
            return $res;
        }
    }

    /**
     * [get_authorizer_option 获取授权方选项信息]
     * @param  string $auth_appid [授权方 appid]
     * @param  string $option_name [选项名称:location_report(地理位置上报选项)、voice_recognize(语音识别开关选项)、customer_service(多客服开关选项)]
     */
    public function get_authorizer_option($auth_appid = '', $option_name = ''){

        //请求地址
        $url = $this->url . "component/api_get_authorizer_option?component_access_token=" . $this->get_component_access_token();

        //发送数据
        $data = [
            "component_appid"         => $this->appid,
            "authorizer_appid"        => $auth_appid,
            "option_name"             => $option_name
        ]; 

        //请求并返回数据
        $res = $this->https_post($url, json_encode($data));
        $res = object_to_array(json_decode($res));
        if(isset($res['errcode']) && $res['errcode'] != 0) {
            //报错,返回空
            return [];
        } else {
            return $res;
        }
    }

    /**
     * [set_authorizer_option 设置授权方选项信息]
     * @param  string $auth_appid [授权方 appid]
     * @param  string $option_name [选项名称:location_report(地理位置上报选项)、voice_recognize(语音识别开关选项)、customer_service(多客服开关选项)]
     * @param  string $option_value [选项值:location_report(0-无上报、1-进入会话时上报、2-每5s上报)、voice_recognize(0-关闭语音识别、1-开启语音识别)、customer_service(0-关闭多客服、1-开启多客服)]
     */
    public function set_authorizer_option($auth_appid = '', $option_name = '', $option_value = 0){

        //请求地址
        $url = $this->url . "component/api_set_authorizer_option?component_access_token=" . $this->get_component_access_token();

        //发送数据
        $data = [
            "component_appid"         => $this->appid,
            "authorizer_appid"        => $auth_appid,
            "option_name"             => $option_name,
            "option_value"            => $option_value
        ]; 

        //请求并返回数据
        $res = $this->https_post($url, json_encode($data));
        $res = object_to_array(json_decode($res));
        if(isset($res['errcode']) && $res['errcode'] != 0) {
            //报错,返回空
            return [];
        } else {
            return $res;
        }
    }

    /**
     * [clear_quota 第三方平台 API 调用次数清零]
     */
    public function clear_quota($component_appid = ''){

        //请求地址
        $url = $this->url . "component/clear_quota?component_access_token=" . $this->get_component_access_token();

        //发送数据
        $data = [
            "component_appid"         => !empty($component_appid) ? $component_appid : $this->appid
        ]; 

        //请求并返回数据
        $res = $this->https_post($url, json_encode($data));
        $res = object_to_array(json_decode($res));
        if(isset($res['errcode']) && $res['errcode'] != 0) {
            //报错,返回空
            return [];
        } else {
            return $res;
        }
    }

    /**
     * [get_agent_thirdplatform_auth description]
     * @param  string $appid    [授权第三方平台的appid]
     * @param  string $back_url [重定向地址URL]
     * @param  [type] $state    [重定向后会带上 state 参数,开发者可以填写任意参数值,最多 128 字节]
     * @param  string $scope    [授权作用域,拥有多个作用域用逗号(,)分隔]
     * @param  string $code     [授权code]
     */
    public function get_agent_thirdplatform_auth($appid = '', $back_url = '', $state = null, $scope = 'snsapi_userinfo', $code = ''){
        //如果不存在code,则去授权
        if(!isset($code) || empty($code)){
            //获取授权code
            $url = $this->getRequestCodeURL($appid, $back_url, $state, $scope);

            //返回数据
            $rdata['redirect_url'] = $url;
            $rdata['msg'] = '获取授权CODE成功';
            $rdata['code'] = 2;
            return $rdata;
        }

        //获取
        $access_token = $this->getAccessToken($appid, 'code', $code);
        if(isset($access_token['errcode'])) {
            //当前code已经被使用过了
            if($access_token['errcode'] == 40163){
                $rdata['code'] = 3;
                $rdata['back_url'] = $back_url;
                $rdata['msg'] = $access_token['errmsg'];
            } else {
                $rdata['code'] = 0;
                $rdata['msg'] = $access_token['errmsg'];
            }
            return $rdata;
        }

        //获取用户信息,如果是:snsapi_userinfo 则获取用户完整信息,如果是静默授权 snsapi_base 则只获取openid
        if($scope == 'snsapi_userinfo'){
            $userinfo = $this->getUserInfo($access_token);
            if(isset($userinfo['errcode'])) {
                $rdata['code'] = 0;
                $rdata['msg'] = $userinfo['errmsg'];
                return $rdata;
            }
        } else {
            $userinfo['openid'] = $access_token['openid'];
        }

        //设置用户信息值
        $userinfo['state'] = $state;
        $userinfo['code'] = $code;
        $userinfo['back_url'] = $back_url;
        $userinfo['unionid']  = (isset($userinfo['unionid']) && !empty($userinfo['unionid'])) ? $userinfo['unionid'] : '';

        //返回数据
        $rdata['code'] = 1;
        $rdata['data'] = $userinfo;
        $rdata['msg'] = '获取用户信息成功';
        return $rdata;
    }

    /**
     * [getRequestCodeURL 获取授权code的URL]
     * @param  string $appid        [公众号的 appid]
     * @param  [type] $redirect_uri [重定向地址,需要 urlencode,这里填写的应是服务开发方的回调地址]
     * @param  [type] $state        [重定向后会带上 state 参数,开发者可以填写任意参数值,最多 128 字节]
     * @param  string $scope        [授权作用域,拥有多个作用域用逗号(,)分隔]
     */
    public function getRequestCodeURL($appid = '', $redirect_uri, $state = null, $scope = 'snsapi_userinfo'){
        
        //参数
        $query = array(
            'appid'           => $appid,
            'redirect_uri'    => $redirect_uri,
            'response_type'   => 'code',
            'scope'           => $scope,
            'component_appid' => $this->appid
        );

        //处理state
        if(!is_null($state) && preg_match('/[a-zA-Z0-9]+/', $state)){
            $query['state'] = $state;
        }

        //组装url
        $query = http_build_query($query);

        //返回url
        return "{$this->requestCodeURL}?{$query}#wechat_redirect";
    }

    /**
     * [getAccessToken 获取access_token,用于后续接口访问]
     * @param  string $type [description]
     * @param  [type] $code [description]
     * @return [type]       [description]
     */
    public function getAccessToken($appid = '', $type = 'client', $code = null){
        
        //组装参数
        $query = array(
            'appid'                  => $appid,
            'component_appid'        => $this->appid,
            'component_access_token' => $this->get_component_access_token()
        );

        //判断类型
        switch ($type) {
            case 'client':
                $query['grant_type'] = 'client_credential';
                $url = "{$this->url}token";
                break;

            case 'code':
                $query['code'] = $code;
                $query['grant_type'] = 'authorization_code';
                $url = "{$this->oauthApiURL}/oauth2/component/access_token";
                break;
        }

        //组装url
        $query = http_build_query($query);
        $url = $url ."?". $query;

        //请求并返回数据
        $res = $this->https_get($url);
        return object_to_array(json_decode($res));
    }

    /**
     * 获取授权用户信息
     * @param  string $token acess_token
     * @param  string $lang  指定的语言
     * @return array         用户信息数据,具体参见微信文档
     */
    public function getUserInfo($token = [], $lang = 'zh_CN'){

        //组装参数
        $query = array(
            'access_token' => $token['access_token'],
            'openid'       => $token['openid'],
            'lang'         => $lang,
        );

        //组装url
        $url = "{$this->oauthApiURL}/userinfo";
        $query = http_build_query($query);
        $url = $url ."?". $query;

        //返回数据
        $res = $this->https_get($url);
        return object_to_array(json_decode($res));
    }

    /**
     * [get_authorizer_token 获取/刷新接口调用令牌]
     * @param  string $authorizer_appid         [第三方平台 appid]
     * @param  string $authorizer_refresh_token [授权方 appid]
     * @return [type]                           [刷新令牌,获取授权信息时得到]
     */
    public function get_authorizer_token($authorizer_appid = '', $authorizer_refresh_token = ''){

        //获取缓存
        $authorizer_access_token = cache('authorizer_access_token');

        //如果不存在缓存则新生成
        if(empty($authorizer_access_token)){

            //请求地址
            $url = $this->url . "component/api_authorizer_token?component_access_token=" . $this->get_component_access_token();

            //发送数据
            $data = [
                "component_appid"           => $this->appid,
                "authorizer_appid"          => $authorizer_appid,
                "authorizer_refresh_token"  => $authorizer_refresh_token
            ]; 

            //请求并返回数据
            $res = $this->https_post($url, json_encode($data));
            $res = object_to_array(json_decode($res));
            if(isset($res['errcode']) && $res['errcode'] != 0) {
                //报错,返回空
                $authorizer_access_token = [];
            } else {
                $authorizer_access_token = $res;
                cache('authorizer_access_token', $authorizer_access_token, 7000);
            }
        }

        //返回数据
        return $authorizer_access_token;
    }

    /**
     * [getSignPackage 获取签名信息]
     */
    public function getSignPackage($authorizer_appid = '', $ticket = '', $url = '') {
        //验证
        if(empty($authorizer_appid) || empty($ticket)){
            return [];
        }

        //票据
        $jsapiTicket = $ticket;

        // 注意 URL 一定要动态获取,不能 hardcode.
        if(empty($url)){
            $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
            $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
        } else {
            //被转义过的url与当前网址保持一致
            $url = htmlspecialchars_decode($url);
        }

        $timestamp = time();
        $nonceStr = $this->createNonceStr();

        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
        $signature = sha1($string);
        $signPackage = array(
           "appId"     => $authorizer_appid,
           "nonceStr"  => $nonceStr,
           "timestamp" => $timestamp,
           "url"       => $url,
           "signature" => $signature,
           "rawString" => $string
        );
        return $signPackage; 
    }

    /**
     * [createNonceStr 创建随机数]
     * @param  integer $length [长度]
     */
    public 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;
    }

    /**
     * [getJsApiTicket 获取微信JS接口的临时票据]
     * @param  string $authorizer_appid [授权authorizer_appid]
     */
    public function getJsApiTicket($authorizer_appid = '', $authorizer_refresh_token = '') {

        //获取/刷新接口调用令牌
        $access_token = $this->get_authorizer_token($authorizer_appid, $authorizer_refresh_token);
        if(empty($access_token)){
            $rdata['errcode'] = 52000;
            $rdata['errmsg'] = '获取access_token异常';
            return $rdata;
        }

        //组装参数
        $query = array(
            'access_token' => $access_token['authorizer_access_token'],
            'type'         => 'jsapi'
        );

        //组装url
        $url = "{$this->url}ticket/getticket";
        $query = http_build_query($query);
        $url = $url ."?". $query;

        //返回数据
        $res = $this->https_get($url);
        return object_to_array(json_decode($res));
    }

    /**
     * [decryptMsg 消息解密]
     */
    public function decryptMsg(){

        //时间戳
        $timeStamp = empty($_GET['timestamp']) ? '' : trim($_GET['timestamp']);

        //临时数据
        $nonce = empty($_GET['nonce']) ? '' : trim ($_GET['nonce']);

        //消息签名
        $msg_sign = empty($_GET['msg_signature']) ? "" : trim($_GET['msg_signature']);

        //获取加密的数据
        $encryptMsg = file_get_contents('php://input');

        //实例化加密类
        $pc = new \wechat\crypt\WXBizMsgCrypt($this->token, $this->key, $this->appid);

        //xml对象解析
        $postArr = xml2array($encryptMsg);

        //格式化xml
        $format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
        $from_xml = sprintf($format, $postArr['Encrypt']);

        //第三方收到公众号平台发送的消息
        $msg = '';

        //解密
        $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $from_xml, $msg);

        //返回数据
        if($errCode == 0) {
            //xml对象解析
            $data = xml2array($msg);
            return $data;
        } else {
            return [];
        }
    }

    /**
     * [response 发送消息]
     * @param  string $msg     [消息]
     * @param  array  $dataMsg [接受的原始数据]
     * @param  array  $dataMsg [消息类型]
     */
    public function response($msg = '', $dataMsg = [], $type = 'text'){

        //时间戳
        $timeStamp = time();

        //临时
        $nonce = time();

        /* 基础数据 */
        $data = array(
            'ToUserName'   => $dataMsg['FromUserName'],
            'FromUserName' => $dataMsg['ToUserName'],
            'CreateTime'   => $timeStamp,
            'MsgType'      => $type,
            'Content'      => $msg
        );

        //转换数据为XML
        $xml = new \SimpleXMLElement('<xml></xml>');
        self::data2xml($xml, $data);
        $xml = $xml->asXML();

        //加密
        $pc = new \wechat\crypt\WXBizMsgCrypt($this->token, $this->key, $this->appid);
        $encryptMsg = '';
        $pc->encryptMsg($xml, $timeStamp, $nonce, $encryptMsg);
        exit($encryptMsg);
    }

    /**
     * [custom_send 给指定用户推送信息]
     * @param  string $authorizer_access_token [access_token]
     * @param  array  $dataMsg                 [消息数据]
     * @param  string $type                    [类型]
     */
    public function custom_send($authorizer_access_token  ='', $dataMsg = [], $type = 'text'){
        
        //基础数据
        $send_data = array(
            'touser'  => $dataMsg['FromUserName'],
            'msgtype' => $type,
            'text'    => [
                'content' => $dataMsg['query_auth_code'] . '_from_api'
            ]
        );

        //file_put_contents('4.txt', json_encode($send_data), FILE_APPEND);

        //编码
        $send_data = urldecode(json_encode($send_data));

        //组装参数
        $query = array(
            'access_token' => $authorizer_access_token,
        );

        //组装url
        $url = "{$this->url}message/custom/send";
        $query = http_build_query($query);
        $url = $url ."?". $query;

        //file_put_contents('5.txt', json_encode($url), FILE_APPEND);

        //返回数据
        $res = $this->https_post($url, $send_data);
        return object_to_array(json_decode($res));
    }

    /**
     * [get_user_info 获取指定用户的详细信息]
     * @param  string $openid [openid]
     * @param  string $lang   [语言]
     */
    public function get_user_info($authorizer_appid = '', $authorizer_refresh_token = '', $openid = '', $lang = 'zh_CN'){

        //获取/刷新接口调用令牌
        $access_token = $this->get_authorizer_token($authorizer_appid, $authorizer_refresh_token);
        if(empty($access_token)){
            return [];
        }

        //组装参数
        $query = array(
            'access_token' => $access_token['authorizer_access_token'],
            'openid'       => $openid,
            'lang'         => $lang
        );

        //组装url
        $url = "{$this->url}user/info";
        $query = http_build_query($query);
        $url = $url ."?". $query;

        //返回数据
        $res = $this->https_get($url);
        return object_to_array(json_decode($res));
    }

    /**
     * 数据XML编码
     * @param  object $xml  XML对象
     * @param  mixed  $data 数据
     * @param  string $item 数字索引时的节点名称
     * @return string
     */
    protected static function data2xml($xml, $data, $item = 'item') {
        foreach ($data as $key => $value) {
            /* 指定默认的数字key */
            is_numeric($key) && $key = $item;

            /* 添加子元素 */
            if(is_array($value) || is_object($value)){
                $child = $xml->addChild($key);
                self::data2xml($child, $value, $item);
            } else {
                if(is_numeric($value)){
                    $child = $xml->addChild($key, $value);
                } else {
                    $child = $xml->addChild($key);
                    $node  = dom_import_simplexml($child);
                    $cdata = $node->ownerDocument->createCDATASection($value);
                    $node->appendChild($cdata);
                }
            }
        }
    }
    
    /**
     * [get_component_access_token 获取令牌:component_access_token,每个令牌是存在有效期(2小时)的,且令牌的调用不是无限制的,请第三方平台做好令牌的管理,在令牌快过期时(比如1小时50分)再进行刷新。所以要对component_access_token做好本地缓存]
     */
    private function get_component_access_token() {

        //保存 component_access_token
        $component_access_token = cache('component_access_token');

        //空的情况下生成缓存
        if(empty($component_access_token)){
            //令牌请求地址
            $url = $this->url . "component/api_component_token";

            //验证票据 component_verify_ticket
            $where = [];
            $where[] = ['id', '=', 1];
            $component_verify_ticket = \Db::name('wechat_thirdplatform_ticket')->where($where)->value('ticket');

            //发送数据
            $data = [
                "component_appid"         => $this->appid,
                "component_appsecret"     => $this->appsecret,
                "component_verify_ticket" => $component_verify_ticket
            ];

            //请求并返回数据
            $res = $this->https_post($url, json_encode($data));
            $res = object_to_array(json_decode($res));
            if(isset($res['errcode']) && $res['errcode'] != 0) {
                //报错,返回空
                $component_access_token = '';
            } else {
                //获取 component_access_token
                $component_access_token = $res['component_access_token'];

                //生成缓存
                cache('component_access_token', $component_access_token, 6600);
            }   
        }

        //返回数据
        return $component_access_token;
    }

    /**
    * [get_pre_auth_code 获取预授权码]
    */
    private function get_pre_auth_code() {

        //请求地址
        $url = $this->url . "component/api_create_preauthcode?component_access_token=" . $this->get_component_access_token();

        //发送数据
        $data = [
            "component_appid"         => $this->appid
        ]; 

        //请求并返回数据
        $res = $this->https_post($url, json_encode($data));
        $res = object_to_array(json_decode($res));
        if(isset($res['errcode']) && $res['errcode'] != 0) {
            //报错,返回空
            return '';
        } else {
            return $res['pre_auth_code'];
        }
    }

    /**
     * [https_post curl处理post]
     * @param  [type] $url  [地址]
     * @param  [type] $data [json数据]
     */
    private function https_post($url, $data) {

        //初始化curl
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        if (!empty($data)){
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }

        //设置选项
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);

        //返回数据
        return $output;
    }

    /**
     * [https_get curl处理get]
     * @param  [type] $url [地址]
     */
    private function https_get($url) {

        //初始化curl
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($curl, CURLOPT_HEADER, FALSE) ;
        curl_setopt($curl, CURLOPT_TIMEOUT,60);
        if (curl_errno($curl)) {
            return 'Errno'.curl_error($curl);
        } else {
            $result = curl_exec($curl);
        }
        curl_close($curl);

        //返回数据
        return $result;
    }
}

三、微信第三方平台控制器类:实例化第三方平台处理基类来实现授权消息、公众号消息事件接收、获取第三方公众号授权、代公众号发起授权(保证是有授权权限,正常是认证过的服务号或者是国家机关媒体类订阅号)、代公众号的JSSDK实现

<?php
/**
 * Author: [ CopyLian ]
 * Date: [ 2017.02.23 ]
 * Email: [ copylian@aikehou.com ]
 * Site: [ http://www.copylian.com ]
 * Description [ 微信第三方平台控制器 ]
 */
namespace app\wechat\controller;
use wechat\Wechat;
use wechat\WechatAuth;

class Thirdplatform extends Base {

    //第三方平台前缀
    private $prefix = 'test_';

    //授权公众号前缀
    //private $prefix_account = 'many_'; //正式机
    private $prefix_account = ''; //测试机

    //红包领取地址
    private $redpacket = '';

	/**
     * [initialize 初始化]
     */
	public function initialize() {
        //继承
		parent::initialize();
	}

    /**
     * [getauth 获取红包领取微信授权]
     */
    public function getauth(){
        if($this->request->isPost()){

            //获取参数
            $params = $this->request->param();

            //验证地址
            if(!isset($params['back_url']) || empty($params['back_url'])){
                $rdata['code'] = 0;
                $rdata['msg'] = lang('l_back_url_error');
                return $rdata;
            }

            $state = 'hltt';

            //处理带参数的url
            $back_url = htmlspecialchars_decode($params['back_url']);

            //获取授权,并获取用户信息
            $auth_data = $this->auth($back_url, $state, 'snsapi_base', $this->prefix_account);
            if($auth_data['code'] == 1){
                //将数据写入数据库
                $userinfo = $auth_data['data'];
      
                //返回        
                $rdata['code'] = 1;
                $rdata['data'] = $userinfo;
                $rdata['msg'] = lang('l_setdata_success');
               
                //返回数据
                return json($rdata);
            } else {
                return json($auth_data);
            }
        }
    }

    /**
     * [getSign 获取微信签名包]
     */
    public function getSign() {
        //获取参数
        $params = $this->request->param();

        //跨域
        if(isset($params['url'])){
            $url = $params['url'];
        } else{
            $url = $this->redpacket;
        }

        //设置分享数据缓存
        $cache_name = md5($url);
        $guess_share_data = cache($cache_name);
        if (empty($guess_share_data)) {
            $guess_share_data = $this->getSignPackage($url);

            //设置缓存
            cache($cache_name, $guess_share_data, 7200);
        }

        //微信签名包
        $data['code'] = 1;
        $data['msg'] = lang('l_get_data_success');
        $data['data'] = $guess_share_data;
        return json($data);
    }

    /**
     * [sysmessage 授权事件接收URL]
     */
    public function sysmessage() {

        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //消息解密
        $data = $Thirdplatform->decryptMsg();

        if(!empty($data)){
            switch ($data['InfoType']) {
                //授权凭证
                case 'component_verify_ticket':
                    $component_verify_ticket = $data['ComponentVerifyTicket'];

                    //票据写入数据库
                    $update_data = [];
                    $update_data['id'] = 1;
                    $update_data['ticket'] = $component_verify_ticket;
                    $update_data['update_time'] = date("Y-m-d H:i:s");
                    \Db::name('wechat_thirdplatform_ticket')->update($update_data);

                    //返回成功消息
                    echo 'success';
                    break;
                //取消授权
                case 'unauthorized':

                    //查询数据是否存在
                    if(isset($data['AppId']) && isset($data['AuthorizerAppid'])){
                        $where = [];
                        $where[] = ['appid', '=', $data['AppId']];
                        $where[] = ['authorizer_appid', '=', $data['AuthorizerAppid']];
                        $thirdplatform = \Db::name('wechat_thirdplatform')->field('id')->where($where)->find();
                        if(!empty($thirdplatform)){
                            //更新授权状态为-1
                            $update_data = [];
                            $update_data['id'] = $thirdplatform['id'];
                            $update_data['status'] = -1;
                            $update_data['update_time'] = date("Y-m-d H:i:s");
                            \Db::name('wechat_thirdplatform')->update($update_data);
                        }
                    }
                    break;
                //授权
                case 'authorized':
                    //无操作
                    break;
                //更新授权
                case 'updateauthorized':

                    //查询数据是否存在
                    if(isset($data['AppId']) && isset($data['AuthorizerAppid'])){
                        $where = [];
                        $where[] = ['appid', '=', $data['AppId']];
                        $where[] = ['authorizer_appid', '=', $data['AuthorizerAppid']];
                        $thirdplatform = \Db::name('wechat_thirdplatform')->field('id')->where($where)->find();
                        if(!empty($thirdplatform)){
                            //更新授权次数
                            $update_data = [];
                            $update_data['id'] = $thirdplatform['id'];
                            $update_data['auth_count'] = \Db::raw('auth_count+1');
                            $update_data['update_time'] = date("Y-m-d H:i:s");
                            \Db::name('wechat_thirdplatform')->update($update_data);
                        }
                    }
                    break;
            }
        }
    }

    /**
     * [eventmsg 消息与事件接收URL]
     */
    public function eventmsg() {

        //获取参数
        $params = $this->request->param();
        if(!isset($params['appid']) || empty($params['appid'])){
            exit();
        }

        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //消息解密
        $data = $Thirdplatform->decryptMsg();
        //file_put_contents('0.txt', json_encode($data), FILE_APPEND);

        //处理消息事件
        if(!empty($data)){
            //第三方平台全网审核发布模拟测试普通消息
            if($data['MsgType'] == 'text' && isset($data['Content']) && $data['Content'] == 'TESTCOMPONENT_MSG_TYPE_TEXT'){
                $Thirdplatform->response('TESTCOMPONENT_MSG_TYPE_TEXT_callback', $data);
            }

            //第三方平台全网审核发布模拟测试Api消息
            if($data['MsgType'] == 'text' && isset($data['Content']) && preg_match('/QUERY_AUTH_CODE:/', $data['Content'])){
                //获得模拟的auth_code
                $query_auth_code = str_replace("QUERY_AUTH_CODE:", "", $data['Content']);
                //file_put_contents('1.txt', json_encode($query_auth_code), FILE_APPEND);

                //通过code获取授权信息
                $auth_code_info = $Thirdplatform->get_query_auth($query_auth_code);
                //file_put_contents('2.txt', json_encode($auth_code_info), FILE_APPEND);

                //发送客服消息
                $data['query_auth_code'] = $query_auth_code;
                //file_put_contents('3.txt', json_encode($data), FILE_APPEND);
                $res = $Thirdplatform->custom_send($auth_code_info['authorizer_access_token'], $data);
                //file_put_contents('6.txt', json_encode($res), FILE_APPEND);
                exit();
            }

            //1、查询当前授权appid的信息,确认是否已授权
            $where = [];
            $where[] = ['authorizer_appid', '=', $params['appid']];
            $where[] = ['status', '=', 1]; //已授权
            $platform = \Db::name('wechat_thirdplatform')->field('id,appid,authorizer_appid,func_info,authorizer_info')->where($where)->findOrEmpty();
            //file_put_contents('1.txt', json_encode($platform), FILE_APPEND);
            if(empty($platform)){
                exit();
            }
            //解析授权信息
            if(!empty($platform['authorizer_info'])){
                $platform['authorizer_info'] = json_decode($platform['authorizer_info'], true);
            }

            //2、验证是否有消息管理权限
            if(empty($platform['func_info'])){
                exit();
            }

            //解析权限,参考:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/func_info.html
            $func_info = json_decode($platform['func_info'], true);
            //file_put_contents('2.txt', json_encode($func_info), FILE_APPEND);
            //验证权限集ID为1的消息管理权限
            if(!in_array(1, $func_info)){
                exit();
            }

            //3、查询是否开通付费了权限功能
            $now_time = date("Y-m-d H:i:s");
            $where = [];
            $where[] = ['platform_id', '=', $platform['id']];
            $where[] = ['start_time', '< time', $now_time]; //时间范围内
            $where[] = ['end_time', '>= time', $now_time]; //时间范围内
            $platform_access = \Db::name('wechat_thirdplatform_access')->where($where)->column('fun_id');
            //file_put_contents('3.txt', json_encode($platform_access), FILE_APPEND);
            if(empty($platform_access)){
                exit();
            }
            
            //处理事件类型
            switch ($data['MsgType']) {
                case 'text':

                    //验证是否开通了口令红包(fun_id = 1)功能权限
                    if(in_array(1, $platform_access)){
                        //1、查询是否开通付费了权限功能:验证是否开通了口令红包(fun_id = 1)功能权限
                        $now_time = date("Y-m-d H:i:s");
                        $where = [];
                        $where[] = ['platform_id', '=', $platform['id']];
                        $where[] = ['fun_id', '=', 1]; //口令红包(fun_id = 1)
                        $where[] = ['start_time', '< time', $now_time]; //时间范围内
                        $where[] = ['end_time', '>= time', $now_time]; //时间范围内
                        $platform_access_red = \Db::name('wechat_thirdplatform_access')->field('id,config,start_time,end_time')->where($where)->find();
                        //file_put_contents('4.txt', json_encode($platform_access_red), FILE_APPEND);
                        if(empty($platform_access_red)){
                            exit();
                        }

                        //2、处理红包口令功能配置信息
                        if(empty($platform_access_red['config'])){
                            exit();
                        }
                        $platform_access_red_config = json_decode($platform_access_red['config'], true);
                        //file_put_contents('5.txt', $platform_access_red['config'], FILE_APPEND);

                        //3、判断活动的开关是否已开
                        if($platform_access_red_config['open'] != 1){
                            //开关未开启
                            exit();
                        }

                        //4、查询口令码
                        $where = [];
                        $where[] = ['platform_id', '=', $platform['id']]; //当前平台
                        $where[] = ['code', '=', $data['Content']];
                        //$where[] = ['status', '=', 1]; //已发放
                        $code = \Db::name('wechat_thirdplatform_p'.$platform['id'].'_redpackets_code')->field('id,code,money,status,platform_id')->where($where)->findOrEmpty();
                        //file_put_contents('6.txt', json_encode($code), FILE_APPEND);
                        if(empty($code)){
                            //未查到口令码
                            exit();
                        }

                        //状态(0-未发放,1-已发放,2-已领取)
                        if($code['status'] != 1){
                            $Thirdplatform->response('此红包已经被领取或已失效', $data);
                        }

                        //5、单个用户最多领取次数,在服务期限内有效,0表示不限制
                        if($platform_access_red_config['limit_receive_num'] > 0){

                            //查询某个用户领取的次数
                            $where = [];
                            $where[] = ['platform_id', '=', $platform['id']]; //当前平台
                            $where[] = ['platform_openid', '=', $data['FromUserName']]; //第三方授权平台公众号的openid
                            $where[] = ['status', '=', 2]; //已领取

                            //限制在多少天内限制最大领取次数,在服务期限内有效,0表示不限制。
                            if($platform_access_red_config['limit_days'] > 0){
                                //判断周期类型:1-几天内,领取时间点到点;2-隔天几天,0点初始化
                                if($platform_access_red_config['limit_days_type'] == 1){
                                    //几天内,领取时间点到点:time() - N * 24*60*60
                                    $start_date = date("Y-m-d H:i:s", time() - $platform_access_red_config['limit_days']*24*60*60);
                                    $end_date = date("Y-m-d H:i:s");
                                    $where[] = ['receive_time', 'between time', [$start_date, $end_date]]; //时间范围内
                                } else if($platform_access_red_config['limit_days_type'] == 2){
                                    //隔天几天,0点初始化:strtotime(date("Y-m-d 23:59:59")) - N * 24*60*60
                                    $start_date = date("Y-m-d H:i:s", strtotime(date("Y-m-d 23:59:59")) - $platform_access_red_config['limit_days']*24*60*60);
                                    $end_date = date("Y-m-d 23:59:59");
                                    $where[] = ['receive_time', 'between time', [$start_date, $end_date]]; //时间范围内
                                }
                            } else {
                                //为限制时间则表示在有效期内
                                $where[] = ['receive_time', 'between time', [$platform_access_red['start_time'], $platform_access_red['end_time']]]; //时间范围内
                            }
                            //file_put_contents('7.txt', json_encode($where), FILE_APPEND);

                            //统计领取次数
                            $count_ids = \Db::name('wechat_thirdplatform_p'.$platform['id'].'_redpackets_code')->where($where)->count('id');
                            //file_put_contents('8.txt', json_encode($count_ids), FILE_APPEND);
                            if($count_ids >= $platform_access_red_config['limit_receive_num']){
                                //file_put_contents('9.txt', json_encode($count_ids), FILE_APPEND);
                                $Thirdplatform->response('您已经领取达到最大次数了,感谢您的支持!', $data);
                            }
                        }

                        //组装加密的串
                        $kldata = [
                            'id' => $code['id'],
                            'code' => $code['code'],
                            'platform_id' => $code['platform_id'],
                            'openid' => $data['FromUserName'] //第三方授权平台公众号用户的openid
                        ];
                        $klkey = encode(json_encode($kldata));
                        //file_put_contents('10.txt', $klkey, FILE_APPEND);
                        
                        //网页版授权
                        //$Thirdplatform->response($this->redpacket . "?klkey=" . $klkey, $data);
                        $Thirdplatform->response('恭喜获得:'.$code['money'].' 元红包,<a href="' . $this->redpacket . "?klkey=" . $klkey .'">点击领取</a>。', $data);
                        
                        //服务器端授权
                        //$Thirdplatform->response(url('redpacket', ['klkey' => $klkey], false, true), $data);
                        //$Thirdplatform->response('恭喜获得:'.$code['money'].' 元红包,<a href="' . url('redpacket', ['klkey' => $klkey], false, true) . '">点击领取</a>。', $data);
                    } else if(in_array(2, $platform_access)){
                        //验证是否开通了关键词红包(fun_id = 2)功能权限
                        exit();
                    }

                    break;
                case 'event':
                    switch ($data['Event']) {
                        case 'subscribe':
                            //处理关注业务逻辑
                            //通过openid拉取用户信息
                            $userinfo = $Thirdplatform->get_user_info($platform['authorizer_appid'], $platform['authorizer_info']['authorization_info']['authorizer_refresh_token'], $data['FromUserName']);
                            //file_put_contents('11.txt', json_encode($userInfo), FILE_APPEND);
                            
                            //拉取信息成功则处理逻辑
                            if(!empty($userinfo) && isset($userinfo['openid'])){
                                //存在则更新,不存在则新增
                                //查询是否以及存在记录
                                $where = [];
                                $where[] = ['openid', '=', $userinfo['openid']];
                                $subscribe_user = \Db::name('wechat_thirdplatform_p'.$platform['id'].'_subscribe_user')->field('id,openid')->where($where)->findOrEmpty();
                                if(!empty($subscribe_user)){
                                    //存在则更新
                                    $update_data = [];
                                    $update_data['id']                = $subscribe_user['id'];
                                    $update_data['platform_id']       = $platform['id'];
                                    $update_data['unionid']           = isset($userinfo['unionid']) ? $userinfo['unionid'] : '';
                                    $update_data['nickname']          = $userinfo['nickname'];
                                    $update_data['sex']               = $userinfo['sex'];
                                    $update_data['country']           = $userinfo['country'];
                                    $update_data['province']          = $userinfo['province'];
                                    $update_data['city']              = $userinfo['city'];
                                    $update_data['headimgurl']        = $userinfo['headimgurl'];
                                    $update_data['userinfo']          = json_encode($userinfo);
                                    $update_data['subscribe_count']   = \Db::raw('subscribe_count + 1');
                                    $update_data['subscribe_last_ip'] = $this->request->ip();
                                    $update_data['subscribe_status']  = 1; //已关注
                                    $update_data['subscribe_time']    = date("Y-m-d H:i:s"); //关注时间
                                    $update_data['update_time']       = date("Y-m-d H:i:s");

                                    //更新
                                    \Db::name('wechat_thirdplatform_p'.$platform['id'].'_subscribe_user')->strict(false)->update($update_data);
                                } else {
                                    //不能存在则新增
                                    $add_data = [];
                                    $add_data['platform_id']       = $platform['id'];
                                    $add_data['unionid']           = isset($userinfo['unionid']) ? $userinfo['unionid'] : '';
                                    $add_data['openid']            = $userinfo['openid'];
                                    $add_data['nickname']          = $userinfo['nickname'];
                                    $add_data['sex']               = $userinfo['sex'];
                                    $add_data['country']           = $userinfo['country'];
                                    $add_data['province']          = $userinfo['province'];
                                    $add_data['city']              = $userinfo['city'];
                                    $add_data['headimgurl']        = $userinfo['headimgurl'];
                                    $add_data['userinfo']          = json_encode($userinfo);
                                    $add_data['subscribe_count']   = \Db::raw('subscribe_count + 1');
                                    $add_data['subscribe_last_ip'] = $this->request->ip();
                                    $add_data['subscribe_status']  = 1; //已关注
                                    $add_data['subscribe_time']    = date("Y-m-d H:i:s"); //关注时间

                                    //写入
                                    \Db::name('wechat_thirdplatform_p'.$platform['id'].'_subscribe_user')->strict(false)->insert($add_data);
                                }
                            }
                            break;
                        case 'unsubscribe':
                            //处理取消关注业务逻辑
                            //存在则更新,不存在则跳过
                            //查询是否以及存在记录
                            $where = [];
                            $where[] = ['openid', '=', $data['FromUserName']];
                            $subscribe_user = \Db::name('wechat_thirdplatform_p'.$platform['id'].'_subscribe_user')->field('id,openid')->where($where)->findOrEmpty();
                            if(!empty($subscribe_user)){
                                //存在则更新
                                $update_data = [];
                                $update_data['id']                  = $subscribe_user['id'];
                                $update_data['subscribe_status']    = 0; //未关注
                                $update_data['unsubscribe_count']   = \Db::raw('unsubscribe_count + 1');
                                $update_data['unsubscribe_last_ip'] = $this->request->ip();
                                $update_data['unsubscribe_time']    = date("Y-m-d H:i:s"); //取消关注时间
                                $update_data['update_time']         = date("Y-m-d H:i:s");

                                //更新
                                \Db::name('wechat_thirdplatform_p'.$platform['id'].'_subscribe_user')->strict(false)->update($update_data);
                            }
                            //file_put_contents('12.txt', json_encode($data), FILE_APPEND);
                            break;
                        case 'CLICK':
                            //点击事件
                            //echo 'success';
                            break;
                    }
                break;
            }
        }
    }

    /**
     * [get_thirdplatform_auth:获取平台授权地址]
     */
    public function get_thirdplatform_auth() {
        
        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //获取授权地址
        $url = $Thirdplatform->get_thirdplatform_auth(url("auth_callback", [], '', true), 1);

        //赋值
        $this->assign('url', $url);

        //生成授权页面
        return $this->fetch();
    }

    /**
     * [auth_callback 授权回调]
     */
    public function auth_callback() {

        //获取参数
        $params = $this->request->param();

        //验证是否存在auth_code
        if(!isset($params['auth_code'])){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '授权异常';
            return json($rdata);
        }
        
        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //授权码获取授权信息
        $auth_code_info = $Thirdplatform->get_query_auth($params['auth_code']);
        if(empty($auth_code_info) || !isset($auth_code_info['authorizer_appid'])){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '授权信息异常';
            return json($rdata);
        }

        //通过授权方appid 获取授权方的帐号基本信息
        $auth_info = $Thirdplatform->get_authorizer_info($auth_code_info['authorizer_appid']);
        if(empty($auth_info) || !isset($auth_info['authorizer_info']) || !isset($auth_info['authorization_info'])){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '授权方的帐号基本信息异常';
            return json($rdata);
        }

        //写入数据
        $add_data = [];

        //第三方平台appid
        $add_data['appid']  = config('thirdplatform.' . $this->prefix . 'thirdplatform_account.appid');

        //授权方appid
        $add_data['authorizer_appid']  = $auth_info['authorization_info']['authorizer_appid'];

        //昵称
        $add_data['nick_name']         = $auth_info['authorizer_info']['nick_name'];

        //头像
        $add_data['head_img']          = $auth_info['authorizer_info']['head_img'];

        //公众号类型(-1-未知,0-订阅号,1-由历史老帐号升级后的订阅号,2-服务号公众号认证类型)
        $add_data['service_type_info'] = $auth_info['authorizer_info']['service_type_info']['id'];

        //公众号认证类型(-1-未认证,0-微信认证,1-新浪微博认证,2-腾讯微博认证,3-已资质认证通过但还未通过名称认证,4-已资质认证通过、还未通过名称认证,但通过了新浪微博认证,5-已资质认证通过、还未通过名称认证,但通过了腾讯微博认证)
        $add_data['verify_type_info']  = $auth_info['authorizer_info']['verify_type_info']['id'];

        //原始ID
        $add_data['user_name']         = $auth_info['authorizer_info']['user_name'];

        //主体名称
        $add_data['principal_name']    = $auth_info['authorizer_info']['principal_name'];

        //公众号所设置的微信号,可能为空
        $add_data['alias']             = $auth_info['authorizer_info']['alias'];

        //用以了解功能的开通状况(0-代表未开通,1-代表已开通)
        $add_data['business_info']     = json_encode($auth_info['authorizer_info']['business_info']);

        //二维码图片的URL,开发者最好自行也进行保存
        $add_data['qrcode_url']        = $auth_info['authorizer_info']['qrcode_url'];

        //授权方的帐号基本信息
        $add_data['authorizer_info']   = json_encode($auth_info);

        //授权给开发者的权限集列表
        $func_info_list = [];
        $func_info = $auth_info['authorization_info']['func_info'];
        if(!empty($func_info)){
            foreach ($func_info as $key => $value) {
                $func_info_list[$key] = $value['funcscope_category']['id'];
            }
        }
        $add_data['func_info']   = json_encode($func_info_list);

        //设置为已授权
        $add_data['status']   = 1;

        //如果存在则更新,不存在这新增
        $where = [];
        $where[] = ['authorizer_appid', '=', $auth_info['authorization_info']['authorizer_appid']];
        $thirdplatform_id = \Db::name('wechat_thirdplatform')->where($where)->value('id');
        if(!isset($thirdplatform_id) || empty($thirdplatform_id) || $thirdplatform_id <= 0){
            //新增
            $res = \Db::name('wechat_thirdplatform')->insert($add_data);
        } else {
            //更新
            $add_data['id'] = $thirdplatform_id;
            $add_data['update_time'] = date("Y-m-d H:i:s");
            $res = \Db::name('wechat_thirdplatform')->update($add_data);
        }

        //返回数据
        if($res){
            //跳转到授权结果页面
            return redirect('auth_result', ['code' => 1]);
        } else {
            //跳转到授权结果页面
            return redirect('auth_result', ['code' => 0]);
        }
    }

    /**
     * [auth_result 授权结果页面]
     */
    public function auth_result() {
        //获取参数
        $params = $this->request->param();

        //设置授权信息
        $res_msg = isset($params['code']) && $params['code'] == 1 ? '授权成功' : '授权失败';

        //返回数据
        $data['code'] = $params['code'];
        $data['msg'] = $res_msg;

        //赋值
        $this->assign('data', $data);

        //渲染
        return $this->fetch();
    }

    /**
     * [get_authorizer_option 获取授权方选项信息]
     */
    public function get_authorizer_option() {

        //获取测试授权平台信息
        $where = [];
        $where[] = ['id', '=', 1];
        $platform_info = \Db::name('wechat_thirdplatform')->where($where)->find();

        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //获取授权方选项信息
        $data = $Thirdplatform->get_authorizer_option($platform_info['authorizer_appid'], 'voice_recognize');
        p($data);
    }

    /**
     * [set_authorizer_option 获取授权方选项信息]
     */
    public function set_authorizer_option() {

        //获取测试授权平台信息
        $where = [];
        $where[] = ['id', '=', 1];
        $platform_info = \Db::name('wechat_thirdplatform')->where($where)->find();

        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //获取授权方选项信息
        $data = $Thirdplatform->set_authorizer_option($platform_info['authorizer_appid'], 'location_report', 2);
        p($data);
    }

    /**
     * [clear_quota 第三方平台调用次数清零]
     */
    public function clear_quota() {

        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //获取授权方选项信息
        $data = $Thirdplatform->clear_quota();
        p($data);
    }

    /**
     * [redpacket 红包领取页面:如果出现微信授权错误,可能是当前公众号没有配置当前域名]
     */
    public function redpacket() {
        //获取参数
        $params = $this->request->param();
        if(!isset($params['klkey']) || empty($params['klkey'])){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '参数异常';
            return json($rdata);
        }
        $kldata = json_decode(decode($params['klkey']), true);

        //默认带参数state
        $state = "redpacket_".$kldata['platform_id'];

        //处理带参数的url
        $back_url = url("redpacket", ["klkey" => $params['klkey']], false, true);

        //获取授权,并获取用户信息
        $auth_data = $this->auth($back_url, $state, 'snsapi_base', $this->prefix_account);
        if($auth_data['code'] == 1){
            //将数据写入数据库
            $userinfo = $auth_data['data'];
  
            //请求红包接口获取红包信息并返回数据
            //发送数据
            $send_data = [
                "klkey"     => $params['klkey'],
                "openid"    => $userinfo['openid'] //领取红包openid(支付公众号用户授权的openid)
            ];
            $result = gethttp(url("get_red_packet", [], false, true), [], json_encode($send_data), "POST");

            //赋值
            $this->assign('data', json_decode($result, true));

            //渲染模板
            return $this->fetch();

        } else if($auth_data['code'] == 2) {
            //去授权跳转
            return redirect($auth_data['redirect_url']);
        } else if($auth_data['code'] == 3){
            //授权code错误,请重新授权
            return redirect($auth_data['back_url']);
        } else {
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '授权错误';
            return json($rdata);
        }
    }

    /**
     * [get_red_packet 领取红包]
     */
    public function get_red_packet() {

        //获取参数
        $params = $this->request->param();

        //验证参数
        if(!isset($params['klkey']) || empty($params['klkey']) || !isset($params['openid']) || empty($params['openid'])) {
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '红包已领取或已过期';
            return json($rdata);
        }

        //解密
        $kldata = json_decode(decode($params['klkey']), true);
        //p($kldata);
        if(!isset($kldata['id']) || !isset($kldata['code']) || !isset($kldata['platform_id'])){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '红包已领取或已过期';
            return json($rdata);
        }

        //查询授权平台信息
        //1、查询当前授权appid的信息,确认是否已授权
        $where = [];
        $where[] = ['id', '=', $kldata['platform_id']];
        $where[] = ['status', '=', 1]; //已授权
        $platform = \Db::name('wechat_thirdplatform')->field('id,appid,authorizer_appid,nick_name,head_img,func_info')->where($where)->findOrEmpty();
        //p($platform);
        if(empty($platform)){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '公众号未授权';
            return json($rdata);
        }

        //2、查询是否开通付费了权限功能:验证是否开通了口令红包(fun_id = 1)功能权限
        $now_time = date("Y-m-d H:i:s");
        $where = [];
        $where[] = ['platform_id', '=', $platform['id']];
        $where[] = ['fun_id', '=', 1]; //口令红包(fun_id = 1)
        $where[] = ['start_time', '< time', $now_time]; //时间范围内
        $where[] = ['end_time', '>= time', $now_time]; //时间范围内
        $platform_access = \Db::name('wechat_thirdplatform_access')->field('id,config')->where($where)->find();
        if(empty($platform_access)){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '未开通服务';
            return json($rdata);
        }

        //3、处理红包口令功能配置信息
        if(!empty($platform_access['config'])){
            $platform_access['config'] = json_decode($platform_access['config'], true);
        }
        //p($platform_access);
        
        //设置背景图
        $bg_img = isset($platform_access['config']['bg_img'][0]['photo']) && !empty($platform_access['config']['bg_img'][0]['photo']) ? $this->request->domain() . $platform_access['config']['bg_img'][0]['photo'] : '';

        //4、查询红包信息
        $where = [];
        $where[] = ['platform_id', '=', $kldata['platform_id']]; //当前平台
        $where[] = ['code', '=', $kldata['code']];
        $where[] = ['id', '=', $kldata['id']];
        $where[] = ['status', '=', 1]; //已发放
        $data = \Db::name('wechat_thirdplatform_p'.$platform['id'].'_redpackets_code')->where($where)->findOrEmpty();
        //p($data);
        if(empty($data)){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '红包已领取或已过期';
            return json($rdata);
        }

        //1、获取红包链接
        //实例化
        $WechatMmpaymk = new \wechat\WechatMmpaymk($this->prefix_account);

        //红包发送方式
        $sen_type = isset($platform_access['config']['type']) && in_array($platform_access['config']['type'], [1, 2]) ? $platform_access['config']['type'] : 1;

        //企业支付到零钱
        if($sen_type == 1){
            //订单编号
            $order_sn = create_order_sn(1, "hbkl".$platform['id']);

            //发送数据
            $send_data = [
                'amount'       => $data['money'] * 100, //单位分
                'openid'       => $params['openid'],
                'order_sn'     => $order_sn,
                'desc'         => $platform['nick_name']."的红包",
                'check_name'   => 'NO_CHECK', //NO_CHECK:不校验真实姓名、FORCE_CHECK:强校验真实姓名
                're_user_name' => '', //收款用户真实姓名,如果check_name设置为FORCE_CHECK,则必填用户真实姓名
            ];

            //p($send_data);
            //exit();
            
            //返回结果
            $res = $WechatMmpaymk->sendMoneyToChange($send_data);
            if($res['code'] == 0){
                $rdata['code'] = 0;
                $rdata['msg'] = $res['msg'];
                return json($rdata);
            } else if($res['code'] == 1){
                if($res['data']['return_code'] == 'SUCCESS' && $res['data']['result_code'] == 'SUCCESS'){

                    //支付成功请修改红包口令码状态为已领取
                    $update_data = [];
                    $update_data['id'] = $data['id'];
                    $update_data['status'] = 2; //已发放
                    $update_data['platform_openid'] = $kldata['openid']; //第三方授权平台公众号用户的openid
                    $update_data['order_sn'] = $order_sn; //订单编号
                    $update_data['receive_openid'] = $params['openid']; //领取红包openid(支付公众号用户授权的openid)
                    $update_data['receive_ip'] = $this->request->ip(); //领取IP
                    $update_data['receive_time'] = date("Y-m-d H:i:s"); //领取时间
                    $update_data['update_time'] = date("Y-m-d H:i:s");
                    \Db::name('wechat_thirdplatform_p'.$platform['id'].'_redpackets_code')->update($update_data);

                    //返回数据
                    $rdata['code'] = 1;
                    $rdata['msg'] = '支付成功';
                    $rdata['data'] = $res['data'];
                    $rdata['redpacket_data'] = ['code' => $kldata['code'], 'money' => $data['money'], 'head_img' => $platform['head_img'], 'bg_img' => $bg_img];
                    return json($rdata);
                } else {
                    $rdata['code'] = 0;
                    $rdata['msg'] = $res['data']['err_code_des'];
                    $rdata['data'] = $res['data'];
                    return json($rdata);
                }
            }

        } else if($sen_type == 2){
            //发红包
            //订单编号
            $order_sn = create_order_sn(1, "hbkl".$platform['id']);

            //发送数据
            $send_data = [
                'amount'       => $data['money'] * 100, //单位分
                'openid'       => $params['openid'],
                'order_sn'     => $order_sn,
                'act_name'     => isset($platform_access['config']['act_name']) ? $platform_access['config']['act_name'] : $platform['nick_name']."的红包", //活动名称
                'send_name'    => $platform['nick_name'],//红包发送者名称
                'wishing'      => isset($platform_access['config']['wishing']) ? $platform_access['config']['wishing'] : "恭喜发财", //红包祝福语
                'remark'       => isset($platform_access['config']['remark']) ? $platform_access['config']['remark'] : "抢到红包了,恭喜发财!" //备注
            ];

            //p($send_data);
            //exit();
            
            //返回结果
            $res = $WechatMmpaymk->redPacketPay($send_data);
            if($res['code'] == 0){
                $rdata['code'] = 0;
                $rdata['msg'] = $res['msg'];
                return json($rdata);
            } else if($res['code'] == 1){
                if($res['data']['return_code'] == 'SUCCESS' && $res['data']['result_code'] == 'SUCCESS'){

                    //支付成功请修改红包口令码状态为已领取
                    $update_data = [];
                    $update_data['id'] = $data['id'];
                    $update_data['status'] = 2; //已发放
                    $update_data['platform_openid'] = $kldata['openid']; //第三方授权平台公众号用户的openid
                    $update_data['order_sn'] = $order_sn; //订单编号
                    $update_data['receive_openid'] = $params['openid']; //领取红包openid(支付公众号用户授权的openid)
                    $update_data['receive_ip'] = $this->request->ip(); //领取IP
                    $update_data['receive_time'] = date("Y-m-d H:i:s"); //领取时间
                    $update_data['update_time'] = date("Y-m-d H:i:s");
                    \Db::name('wechat_thirdplatform_p'.$platform['id'].'_redpackets_code')->update($update_data);

                    //返回数据
                    $rdata['code'] = 1;
                    $rdata['msg'] = '支付成功';
                    $rdata['data'] = $res['data'];
                    $rdata['redpacket_data'] = ['code' => $kldata['code'], 'money' => $data['money'], 'head_img' => $platform['head_img'], 'bg_img' => $bg_img];
                    return json($rdata);
                } else {
                    $rdata['code'] = 0;
                    $rdata['msg'] = $res['data']['err_code_des'];
                    $rdata['data'] = $res['data'];
                    return json($rdata);
                }
            }
        }
    }

    /**
     * [agent_thirdplatform_auth 第三方平台代理公众号发起授权]
     * @param  integer $platform_id [平台ID]
     * @param  string  $state       [重定向后会带上 state 参数,也是做活动参数使用]
     * @param  string  $back_url    [重定向地址,需要 urlencode,这里填写的应是服务开发方的回调地址]
     * @param  string  $scope       [授权作用域,拥有多个作用域用逗号(,)分隔,snsapi_userinfo、snsapi_base]
     * @param  string  $code        [跳转回来的返回码]
     * @return [type]               [description]
     */
    public function agent_thirdplatform_auth($platform_id = 0, $state = 'test', $back_url = '', $scope = 'snsapi_userinfo', $code = ''){
        //验证平台ID参数
        if($platform_id <= 0){
            $rdata['code'] = 0;
            $rdata['msg'] = '平台不存在';
            return json($rdata);
        }

        //验证地址
        if(!isset($back_url) || empty($back_url)){
            $rdata['code'] = 0;
            $rdata['msg'] = '重定向地址URL错误';
            return json($rdata);
        }

        //处理带参数的url
        $back_url = htmlspecialchars_decode($back_url);

        //1、查询授权平台信息
        $where = [];
        $where[] = ['id', '=', $platform_id];
        $where[] = ['status', '=', 1]; //已授权
        $platform = \Db::name('wechat_thirdplatform')->field('id,appid,authorizer_appid,nick_name,head_img,func_info')->where($where)->findOrEmpty();
        if(empty($platform)){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '公众号未授权';
            return json($rdata);
        }

        //2、验证是否有权限
        if(empty($platform['func_info'])){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '公众号授权第三方平台权限';
            return json($rdata);
        }

        //3、解析权限,参考:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/func_info.html
        $func_info = json_decode($platform['func_info'], true);
        if(!in_array(4, $func_info)){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '公众号授权第三方平台权限';
            return json($rdata);
        }
        
        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //获取授权
        $auth_data = $Thirdplatform->get_agent_thirdplatform_auth($platform['authorizer_appid'], $back_url, $state, $scope, $code);
        if($auth_data['code'] == 1){
            //将数据写入数据库
            $userinfo = $auth_data['data'];
            $data = $userinfo;
            $data['userinfo'] = json_encode($userinfo);
            $data['state'] = $state;

            //查询是否以及存在记录
            $table_name = 'wechat_thirdplatform_p'.$platform['id'].'_'.$state.'_user';
            $where = [];
            $where[] = ['openid', '=', $userinfo['openid']];
            $user = \Db::name($table_name)->where($where)->find();

            //写入数据
            $data['count'] = \Db::raw('count+1');
            if(!$user){
                //添加
                $res = \Db::name($table_name)->strict(false)->insert($data);
            } else {
                $data['id'] = $user['id'];
                $data['update_time'] = date("Y-m-d H:i:s");
                $res = \Db::name($table_name)->strict(false)->update($data);
            }
            
            //处理头像跨域
            if(isset($userinfo['headimgurl']) && !empty($userinfo['headimgurl'])){
                $userinfo['headimgurl'] = url('base/getRouteImg',['url'=>encode($userinfo['headimgurl'])],false,true);
            }

            //返回        
            if($res){
               $rdata['code'] = 1;
               $rdata['data'] = $userinfo;
               $rdata['msg'] = '获取用户信息成功';
            } else {
               $rdata['code'] = 0;
               $rdata['msg'] = '获取用户信息失败';
            }
           
            //返回数据
            return json($rdata);
        } else {
            return json($auth_data);
        }
    }

    /**
     * [getSignPackage 获取签名]
     */
    public function getSignPackage($platform_id = 0, $url = ''){

        //验证平台ID参数
        if($platform_id <= 0){
            $rdata['code'] = 0;
            $rdata['msg'] = '平台不存在';
            return json($rdata);
        }

        //验证地址
        if(!isset($url) || empty($url)){
            $rdata['code'] = 0;
            $rdata['msg'] = 'url错误';
            return json($rdata);
        }

        //1、查询授权平台信息
        $where = [];
        $where[] = ['id', '=', $platform_id];
        $where[] = ['status', '=', 1]; //已授权
        $platform = \Db::name('wechat_thirdplatform')->field('id,appid,authorizer_appid,nick_name,head_img,func_info,authorizer_info')->where($where)->findOrEmpty();
        if(empty($platform)){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = '公众号未授权';
            return json($rdata);
        }

        //解析授权信息
        if(!empty($platform['authorizer_info'])){
            $platform['authorizer_info'] = json_decode($platform['authorizer_info'], true);
        }

        //实例化第三方平台类
        $Thirdplatform = new \wechat\Thirdplatform($this->prefix);

        //获取零时票据
        $ticket = $Thirdplatform->getJsApiTicket($platform['authorizer_appid'], $platform['authorizer_info']['authorization_info']['authorizer_refresh_token']);
        if($ticket['errcode'] != 0 && !isset($ticket['ticket'])){
            //返回数据
            $rdata['code'] = 0;
            $rdata['msg'] = $ticket['errmsg'];
            return json($rdata);
        }

        //获取signPackage签名信息
        return $Thirdplatform->getSignPackage($platform['authorizer_appid'], $ticket['ticket'], $url);
    }
}

四、授权签名测试类:测试授权公众号的授权功能、测试授权公众号的JSSDK功能

<?php
/**
 * Author: [ CopyLian ]
 * Date: [ 2017.02.23 ]
 * Email: [ copylian@aikehou.com ]
 * Site: [ http://www.copylian.com ]
 * Description [ 测试接口控制器 ]
 */
namespace app\wechat\controller;
use wechat\Wechat;
use wechat\WechatAuth;

class Test extends Base{

    //授权公众号前缀
    //private $prefix_account = 'many_'; //正式机
    private $prefix_account = ''; //测试机

    /**
     * [initialize 初始化]
     */
    public function initialize(){
        parent::initialize();
    }

    /**
     * [auth 测试第三方平台授权]
     */
    public function getauth(){
        if($this->request->isPost()){

            //获取参数
            $params = $this->request->param();

            //验证地址
            if(!isset($params['back_url']) || empty($params['back_url'])){
                $rdata['code'] = 0;
                $rdata['msg'] = lang('l_back_url_error');
                return $rdata;
            }

            //平台ID
            $platform_id = 2; //平台ID
            $state = 'test'; //重定向后会带上 state 参数、平台活动名称
            //$scope = 'snsapi_userinfo'; //授权作用域:snsapi_userinfo、snsapi_base
            $scope = 'snsapi_base'; //授权作用域:snsapi_userinfo、snsapi_base
            $back_url = $params['back_url']; //重定向URL
            $code = isset($params['code']) ? $params['code'] : '';

            //执行第三方的授权
            $Thirdplatform = new \app\wechat\controller\Thirdplatform();
            $data = $Thirdplatform->agent_thirdplatform_auth($platform_id, $state, $back_url, $scope, $code);
            return $data;
        }
    }

    /**
     * [getSign 测试第三方平台获取微信签名包]
     */
    public function getSign() {
        //获取参数
        $params = $this->request->param();

        //跨域
        if(isset($params['url'])){
            $url = $params['url'];
        } else{
            $url = 'https://h5test.ponyfamily.cn/wechat/index.html';
        }

        //设置分享数据缓存
        $cache_name = md5($url);
        $share_data = cache($cache_name);
        if(empty($share_data)){

            //平台ID
            $platform_id = 2; //平台ID
            $code = isset($params['code']) ? $params['code'] : '';

            //执行第三方的授权
            $Thirdplatform = new \app\wechat\controller\Thirdplatform();
            $share_data = $Thirdplatform->getSignPackage($platform_id, $url);

            //设置缓存
            cache($cache_name, $share_data, 7200);
        }

        //微信签名包
        $data['code'] = 1;
        $data['msg'] = lang('l_get_data_success');
        $data['data'] = $share_data;
        return json($data);
    }

    /**
     * [changePay 企业付款到零钱]
     */
    public function changePay() {
        
        //1、获取红包链接
        //实例化
        $WechatMmpaymk = new \wechat\WechatMmpaymk($this->prefix_account);

        //发送数据
        $send_data = [
            'amount'       => 60, //单位分
            'openid'       => 'oHL30wVEuI58Lgp8dkQmSSW4dU4g',
            'order_sn'     => create_order_sn(1, "qylq"),
            'desc'         => '企业付款到零钱',
            'check_name'   => 'NO_CHECK', //NO_CHECK:不校验真实姓名、FORCE_CHECK:强校验真实姓名
            're_user_name' => '', //收款用户真实姓名,如果check_name设置为FORCE_CHECK,则必填用户真实姓名
        ];
        
        //返回结果
        $res = $WechatMmpaymk->sendMoneyToChange($send_data);
        if($res['code'] == 0){
            $rdata['code'] = 0;
            $rdata['msg'] = $res['msg'];
            return json($rdata);
        } else if($res['code'] == 1){
            if($res['data']['return_code'] == 'SUCCESS' && $res['data']['result_code'] == 'SUCCESS'){
                $rdata['code'] = 1;
                $rdata['msg'] = '支付成功';
                $rdata['data'] = $res['data'];
                return json($rdata);
            } else {
                $rdata['code'] = 0;
                $rdata['msg'] = $res['data']['err_code_des'];
                $rdata['data'] = $res['data'];
                return json($rdata);
            }
        }
    }

    /**
     * [queryChangePay 查询企业付款到零钱]
     */
    public function queryChangePay() {
        
        //1、获取红包链接
        //实例化
        $WechatMmpaymk = new \wechat\WechatMmpaymk($this->prefix_account);

        //发送数据
        $send_data = [
            'order_sn'     => 'qylq00807115204001340'
        ];
        
        //返回结果
        $res = $WechatMmpaymk->queryChange($send_data);
        if($res['code'] == 0){
            $rdata['code'] = 0;
            $rdata['msg'] = $res['msg'];
            return json($rdata);
        } else if($res['code'] == 1){
            if($res['data']['return_code'] == 'SUCCESS' && $res['data']['result_code'] == 'SUCCESS'){
                $rdata['code'] = 1;
                $rdata['msg'] = '查询成功';
                $rdata['data'] = $res['data'];
                return json($rdata);
            } else {
                $rdata['code'] = 0;
                $rdata['msg'] = $res['data']['err_code_des'];
                $rdata['data'] = $res['data'];
                return json($rdata);
            }
        }
    }

    /**
     * [redPacketPay 发送红包]
     */
    public function redPacketPay() {
        
        //1、获取红包链接
        //实例化
        $WechatMmpaymk = new \wechat\WechatMmpaymk($this->prefix_account);

        //发送数据
        $send_data = [
            'amount'       => 30, //单位分
            'openid'       => 'oHL30wVEuI58Lgp8dkQmSSW4dU4g',
            'order_sn'     => create_order_sn(1, "qylq")
        ];
        
        //返回结果
        $res = $WechatMmpaymk->redPacketPay($send_data);
        if($res['code'] == 0){
            $rdata['code'] = 0;
            $rdata['msg'] = $res['msg'];
            return json($rdata);
        } else if($res['code'] == 1){
            if($res['data']['return_code'] == 'SUCCESS' && $res['data']['result_code'] == 'SUCCESS'){
                $rdata['code'] = 1;
                $rdata['msg'] = '支付成功';
                $rdata['data'] = $res['data'];
                return json($rdata);
            } else {
                $rdata['code'] = 0;
                $rdata['msg'] = $res['data']['err_code_des'];
                $rdata['data'] = $res['data'];
                return json($rdata);
            }
        }
    }

    /**
     * [queryRedPacket 查询现金红包]
     */
    public function queryRedPacket() {
        
        //1、获取红包链接
        //实例化
        $WechatMmpaymk = new \wechat\WechatMmpaymk($this->prefix_account);

        //发送数据
        $send_data = [
            'order_sn'     => 'qylq00807151840001406'
        ];
        
        //返回结果
        $res = $WechatMmpaymk->queryRedPacket($send_data);
        if($res['code'] == 0){
            $rdata['code'] = 0;
            $rdata['msg'] = $res['msg'];
            return json($rdata);
        } else if($res['code'] == 1){
            if($res['data']['return_code'] == 'SUCCESS' && $res['data']['result_code'] == 'SUCCESS'){
                $rdata['code'] = 1;
                $rdata['msg'] = '查询成功';
                $rdata['data'] = $res['data'];
                return json($rdata);
            } else {
                $rdata['code'] = 0;
                $rdata['msg'] = $res['data']['err_code_des'];
                $rdata['data'] = $res['data'];
                return json($rdata);
            }
        }
    }
}

五、全网发布与检测微信第三方平台全网发布返回Api、普通文本信息失败解决方案

总结:微信第三方平台很多,这类系统也很多,如:微擎、有赞等,如果时间充裕确实可以自己搞一搞,虽然微信第三方平台的接口有些很蛋疼,但是总归还是能实现,多积攒点经验。

代码附件:微信开放平台中第三方平台api接口的对接与实现.zip

只袄早~~~
感谢你的支持,我会继续努力!
扫码打赏,感谢您的支持!

文明上网理性发言!

  • 还没有评论,沙发等你来抢