微信 服务号开发

Posted 栋的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信 服务号开发相关的知识,希望对你有一定的参考价值。

tp5.0  一个入口文件,一个wx类

//WxAction.php 入口
<?php
/**
 * Created by PhpStorm.
 * User: lxd
 * Date: 17/10/31
 * Time: 18:01
 * 微信请求统一入口,单独的类,无需基础其他类
 */
namespace app\index\controller;

use telerr\Chaoxin;
use think\Config;
use think\Request;
use wx\Wx;

class WxAction
{
    protected $request  = null;

    /**
     * 构造
     */
    public function __construct()
    {

        //绑定请求对象
        $this->request  = Request::instance();

        //接收参数, 验证来源
        $this->getParam();


    }

    /**
     * 验证请求来源
     */
    private function checkFrom()
    {
        if( !$this->request->wx_signature || !$this->request->wx_timestamp || !$this->request->wx_nonce ) {
            exit( 非法请求 );
        }
        $tmpArr         = [$this->request->wx_token, $this->request->wx_timestamp, $this->request->wx_nonce];
        sort( $tmpArr, SORT_STRING );

        if( $this->request->wx_signature != sha1( implode(  ‘‘, $tmpArr ) ) ) {
            exit( 请求非法 );
        }

        if( $this->request->has( echostr ) )
            exit( $this->request->param(echostr) );
    }

    /**
     * 接收参数
     */
    private function getParam()
    {
        $this->request->wx_signature      = $this->request->param( signature, ‘‘, trim );
        $this->request->wx_timestamp      = $this->request->param( timestamp, ‘‘, trim );
        $this->request->wx_nonce          = $this->request->param( nonce, ‘‘, trim );
        $this->request->wx_openid         = $this->request->param( openid, ‘‘, trim );
        $this->request->wx_token          = Config::get( wx.appToken );
        if( Config::get(app_debug ) )
            $this->request->wx_token          = Config::get( wx.appTokenlxdTest );

        //验证请求来源
        $this->checkFrom();
    }

    /**
     * 默认访问方法
     */
    public function index()
    {
        //获取解析后的数据
        $this->request->wxRequest     = Wx::parseXml( $this->request->getInput() );

        $wxObj      = Wx::instance();
        register_shutdown_function( array($wxObj,eventDefault) ); //记日志

        //请求方法
        if( is_array( $this->request->wxRequest ) && !empty( $this->request->wxRequest ) ) {
            $tmpMsgType         = strtolower( $this->request->wxRequest[msgtype] );
            switch( $tmpMsgType ) {
                //事件
                case event :
                    $tmpEvent       = strtolower( $this->request->wxRequest[event] );
                    switch( $tmpEvent ) {
                        case subscribe: //关注事件(直接关注|扫码关注)
                            $wxObj->eventSubscribe();
                            break;
                        case unsubscribe: //取消关注事件
                            $wxObj->eventUnsubscribe();
                            break;
                        case scan: //扫描二维码,已关注
                            $wxObj->eventScan();
                            break;
                        case location: //上报地理位置
                        case click: //点击自定义菜单事件
                            $wxObj->eventClick();
                            break;
                        case view: //点击自定义链接菜单事件
                            $wxObj->viewClickEvn();
                            break;
                        default :
                            //默认动作
                    }
                    break;

                //文本消息
                case text:
                    $wxObj->eventText();
                    break;


                default:
                    $wxObj->noSupprt(); //默认动作
                    break;
            }

            $wxObj      = null;
        }
    }

}
//wx类
<?php
/**
 * Created by PhpStorm.
 * User: lxd
 * Date: 17/11/1
 * Time: 13:33
 * 微信处理类
 */
namespace wx;

use app\bp\controller\User;
use app\common\controller\UserAgencyRalation;
use app\common\model\UserAgenceRelateion;
use Curl\Curl;
use redis\Redis;
use telerr\Chaoxin;
use think\Cache;
use think\Config;
use think\Loader;
use think\Log;
use think\Request;
use think\Session;

class Wx
{
    protected $request      = null;
    protected $cache               = null;
    protected $curl                = null;
    protected static $instance     = null;
    protected $accessTokenCt       = 7000; //微信官方7200,少200秒 jsapi_ticket 也采用该时间
    protected $ipListCt            = 2592000; //30天
    protected $redisKey            = ‘Wx::‘;

    /**
     * 构造方法
     */
    private function __construct()
    {
        if( !$this->request )
            $this->request  = Request::instance();

        //初始化缓存类
        if( !$this->cache ) {
            $this->cache        = Redis::getInstance();
        }

        //初始化缓存类
        if( !$this->curl ) {
            $this->curl     = new Curl();
            $this->curl->setJsonDecoder(false);
        }

    }

    public function __call($method, $args)
    {
        if( method_exists( $this, $method ) ) {
            return call_user_func( [$this,$method], $args );
        }

        $this->myLog( ‘ ?? 调用了不存在的方法‘ . $method . ‘; 参数  ‘ . json_encode( $args ) );

        return false;
    }

    /**
     * 初始化类
     */
    public static function instance()
    {
        if( is_null( self::$instance ) )
            self::$instance     = new static();

        return self::$instance;
    }

    /**
     * 解析xml
     *
     * @param string $xml 数据
     * @return array $return
     */
    public static function parseXml( $xml )
    {
        if( !is_string( $xml ) )
            return [];

        $domobj         = new \DOMDocument();
        if( !$domobj->loadXML( $xml ) ) {
            Log::error( ‘load error ‘. $xml );
            return [];
        }

        $return         = [];
        if( $tagname = $domobj->getElementsByTagName( ‘ToUserName‘ )->item( 0 ) ) {
            $return[‘tuser‘]        =  $tagname->nodeValue;
            $tagname             = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘FromUserName‘ )->item( 0 ) ) {
            $return[‘fuser‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘CreateTime‘ )->item( 0 ) ) {
            $return[‘ctime‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘MsgType‘ )->item( 0 ) ) {
            $return[‘msgtype‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘Event‘ )->item( 0 ) ) {
            $return[‘event‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘EventKey‘ )->item( 0 ) ) {
            $return[‘eventkey‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘Ticket‘ )->item( 0 ) ) {
            $return[‘ticket‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘Latitude‘ )->item( 0 ) ) {
            $return[‘lat‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘Longitude‘ )->item( 0 ) ) {
            $return[‘lon‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘Precision‘ )->item( 0 ) ) {
            $return[‘precision‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘Content‘ )->item( 0 ) ) {
            $return[‘content‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘MsgId‘ )->item( 0 ) ) {
            $return[‘msgid‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        if( $tagname = $domobj->getElementsByTagName( ‘MenuId‘ )->item( 0 ) ) {
            $return[‘menuid‘]        =  $tagname->nodeValue;
            $tagname           = null;
        }

        $domobj         = null;
        return $return;
    }

    /**
     * 获取微信 access_token
     */
    public function getAccessToken()
    {
        $cachename      = ‘access_token_online‘;
        if( Config::get( ‘app_debug‘ ) )
            $cachename      = ‘access_token1‘;

        $accessToken    = $this->cache->get( $this->redisKey . $cachename );
        if( !$accessToken ) {
            if( Config::get(‘app_debug‘) )
                $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/token?grant_type=client_credential&appid=‘ . Config::get( ‘wx.appIDlxdTest‘ ) . ‘&secret=‘ . Config::get( ‘wx.appsecretlxdTest‘ );
            else
                $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/token?grant_type=client_credential&appid=‘ . Config::get( ‘wx.appID‘ ) . ‘&secret=‘ . Config::get( ‘wx.appsecret‘ );

            $info       = $this->sendCurl( $url );
            if( isset( $info[‘access_token‘] ) && $info[‘access_token‘] ) {
                $accessToken        = $info[‘access_token‘];
                if( !$this->cache->set( $this->redisKey . $cachename, $accessToken, $this->accessTokenCt ) ) {
                    $this->myLog( ‘access_token 写入缓存失败‘ );
                    return true;
                }
            }
        }

        return $accessToken;
    }

    /**
     * 关注事件(直接关注|扫码关注)
     *
     * @return bool
     */
    public function eventSubscribe()
    {
        $parentId   = 0;
        $eventKey   = isset( $this->request->wxRequest[‘eventkey‘] ) ? strtolower( $this->request->wxRequest[‘eventkey‘] ) : ‘‘;
        if( $eventKey ) {
            if( strpos( $eventKey, ‘qrscene_‘ ) !== 0 ) {
                $this->myLog( ‘用户关注获取 $eventKey 值不是qrscene_开头,请检查‘ . var_export( $this->request->wxRequest, true ) );
            }

            $parentId   = str_replace( ‘qrscene_‘, ‘‘, $eventKey );
        }


        $info       = $this->getUserinfoByOpenid( $this->request->wxRequest[‘fuser‘] );
        if( $info ) {
            //如果不存在,那么自己生产一个
            if( !isset( $info[‘unionid‘] ) ) {
                $info[‘unionid‘]      = ‘__‘ . md5( $this->request->wxRequest[‘fuser‘] . $this->request->wxRequest[‘tuser‘] );
                if( !Config::get(‘app_debug‘) )
                    $this->myLog( ‘未获取到unionid值,info‘ . var_export( $info, true ) );
            }

            $this->request->wxRegData       = [
                ‘user‘      => $info,
                ‘param‘     => $this->request->wxRequest,
                ‘openid‘    => isset( $this->request->wxRequest[‘fuser‘] ) ? $this->request->wxRequest[‘fuser‘] : $this->request->wx_openid,
            ];

            //判断数据库是否存在用户信息,存在添加日志;不存在 添加用户,添加日志
            $model      = Loader::controller( ‘app\common\controller\User‘ );
            $isExi      = $model->isExiByUnionid();

            if( $isExi === false ) {
                $this->myLog( ‘isExiByUnionid ‘ . var_export( $model->isExiByUnionid(), true ) . ‘wxdata ‘ . $this->request->wxRegData );
            }else if( !$isExi ) {
                $res        = $model->addOneUserByWx();

                if( !$res ) {
                    $this->myLog( ‘微信注册用户失败,‘ . $this->request->modelErr . ‘data == ‘ . json_encode( $this->request->getInput() ) );
                    return false;
                }

                $time       = time();
                //如果有父id 推荐人,给推荐人发送消息
                if( $parentId ) {
                    //写入 关系表
                    $relationModel      = Loader::model( ‘UserAgenceRelateion‘ );
                    $relationAdd        = [
                        ‘uid‘       => $parentId,
                        ‘cuid‘      => $res,
                        ‘sour‘      => UserAgenceRelateion::SOURWX,
                        ‘monrate‘   => \app\common\model\User::BMSSONNORMALAGENCYMONRATE,
                        ‘atm‘       => $time,
                        ‘montype‘   => UserAgenceRelateion::MONFROMBM
                    ];
                    if( !$relationModel->addOneData( $relationAdd ) ) {
                        $this->myLog( ‘代理二级关系入库失败‘ . json_encode( $relationAdd ) );
                    }

                    $template_id        = ‘‘;
                    if( Config::get( ‘app_debug‘ ) )
                        $template_id    = ‘lgR8cs4krnc0DRYYU0liwesaFbd2iWax-IJywTWqdAw‘;

                    $touser         = \app\common\controller\User::getOpenidByUid( $parentId );
                    $url            = ‘‘;
                    $nickName       = isset( $info[‘nickname‘] ) ? $info[‘nickname‘] : ‘该用户无昵称‘;
                    $data           = [
                        ‘first‘     => [‘value‘ => ‘你邀请的用户接受了你的邀请‘, ‘color‘ => ‘#337ab7‘],
                        ‘keyword1‘  => [‘value‘ => ‘微信昵称:‘ . $nickName, ‘color‘ => ‘#337ab7‘],
                        ‘keyword2‘  => [‘value‘ => date( ‘Y-m-d H:i‘, $time ), ‘color‘ => ‘#337ab7‘],
                        ‘remark‘    => [‘value‘ => ‘感谢您的关注‘, ‘color‘ => ‘#337ab7‘]
                    ];
                    $this->_sendWxTemplate( $template_id, $touser, $url, $data );
                }
            }

            $helpurl        = $this->makeUrlAndShort( ‘/index/user/applyAgency‘ );
            $content        = <<<EOF
亲,终于等到你~
轻松赚钱的秘诀在这里等待您的到来,海量优质商品超高的优惠力度

回复“找XX”系统会自动推送商品的优惠信息,如“找牙刷”

回复“签到”即可签到,签到成功近期会有神秘奖励,敬请期待

申请成为代理,分享商品轻松赚钱 $helpurl
EOF;

            $this->_sendWXText( $content );

            return true;
        }

        return $info;
    }

    /**
     * 取消关注事件
     *
     */
    public function eventUnsubscribe() {

    }

    /**
     * 获取用户基本资料,基于openid
     */
    public function getUserinfoByOpenid( $openid )
    {
        $rk         = ‘Wx:getUserinfoByOpenid:‘ . $openid;
        $info       = $this->cache->get( $rk );
        if( $info )
            return $info;

        //获取用户信息
        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!eventSubscribe 方法执行异常‘ . json_encode( func_get_args() ) );
            return false;
        }

        if( Config::get(‘app_debug‘) )
            $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/user/info?access_token=‘.$accessToken.‘&openid=‘.$openid.‘&lang=zh_CN‘;
        else
            $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/user/info?access_token=‘.$accessToken.‘&openid=‘.$openid.‘&lang=zh_CN‘;

        $info       = $this->sendCurl( $url );
        if( !$info ) {
            $this->myLog( ‘获取用户基本资料失败, url = ‘ . $url . ‘, info = ‘. var_export( $url, true ) );
            return true;
        }

        $this->cache->set( $rk, $info, 86400 );
        return $info;
    }

    /**
     * 记录log到数据库
     */
    public function eventDefault()
    {
        if( !$this->request->wxRegData ) {
            $this->request->wxRegData       = [
                ‘param‘     => $this->request->wxRequest,
                ‘openid‘    => isset( $this->request->wxRequest[‘fuser‘] ) ? $this->request->wxRequest[‘fuser‘] : $this->request->wx_openid,
            ];
        }

        $model      = Loader::controller( ‘app\common\controller\Wxlog‘ );
        $res        = $model->addOne();
        if( !$res ) {
            $this->myLog( ‘微信log记录失败,‘ . $this->request->modelErr . ‘data == ‘ . json_encode( $this->request->wxRegData ) );
            return false;
        }
        return true;
    }

    /**
     * log
     */
    public function myLog( $msg, $isback = true )
    {
        Log::init( [
            ‘path‘  => \think\Config::get( ‘thirdlog‘ ) . ‘Wx‘ . DIRECTORY_SEPARATOR,
            ‘file_size‘     => ‘209715200‘ //200m
        ] );
        Log::write( $msg );
        Chaoxin::sendTextMsg( $msg, $isback );
    }

    /**
     * curl
     */
    public function sendCurl( $url, $type = ‘get‘, $data = [], $postJson = true )
    {

        if( $type == ‘get‘ )
            $this->curl->get( $url );
        else{
            if( $postJson )
                $this->curl->setHeader( ‘Content-Type‘, ‘application/json‘ );

            $this->curl->post( $url, $data );
        }
        if( $this->curl->error ) {
            $this->myLog(‘curl 请求异常, url ===‘ . $url . ‘; error = ‘ . $this->curl->errorCode . ‘|‘ . $this->curl->errorMessage);
            return false;
        } else {
            $info       =  json_decode( $this->curl->response, true );
            if( $info ) {
                if( isset( $info[‘errcode‘] ) && $info[‘errcode‘] != 0 ) {
                    if( $info[‘errcode‘] == ‘40001‘ ) { //获取access_token 失败

                        $cachename      = ‘access_token_online‘;
                        if( Config::get( ‘app_debug‘ ) )
                            $cachename      = ‘access_token1‘;
                        $this->cache->del( $this->redisKey . $cachename );

                        $this->getAccessToken();
                    }

                    if( $info[‘errcode‘] != ‘40163‘ && $info[‘errcode‘] != ‘40029‘ ) //code 失效,网页在微信外打开了
                        $this->myLog( ‘请求结果失败, url = ‘ . $url . ‘info == ‘ . var_export( $info, true ) );

                    return false;
                }
                return $info;
            }else {
                $this->myLog( ‘请求结果异常, url = ‘ . $url . ‘info == ‘ . var_export( $info, true ) );
                return false;
            }
        }
    }

    /**
     * 网页授权,获取用户信息
     */
    public function webScope()
    {
        $code       = $this->request->param( ‘code‘, ‘‘ );
        if( !$code ) {
            $this->myLog( ‘未获取到code,wx.php webScope 方法非法进入‘ );
            return false;
        }

        if( Config::get( ‘app_debug‘ ) )
            $url    = Config::get(‘wx.apiUrl‘) . "/sns/oauth2/access_token?appid=".Config::get(‘wx.appIDlxdTest‘)."&secret=".Config::get(‘wx.appsecretlxdTest‘)."&code=".$code."&grant_type=authorization_code";
        else
            $url    = Config::get(‘wx.apiUrl‘) . "/sns/oauth2/access_token?appid=".Config::get(‘wx.appID‘)."&secret=".Config::get(‘wx.appsecret‘)."&code=".$code."&grant_type=authorization_code";

        $info       = $this->sendCurl( $url );
        if( !$info || !isset( $info[‘openid‘] ) || empty( $info[‘openid‘] ) ) {
            if( isset($info[‘errcode‘]) ) { //40163 code been used;网页在浏览器上打开
                if( $info[‘errcode‘] != ‘40163‘ && $info[‘errcode‘] != ‘40029‘ )
                    $this->myLog( ‘网页授权url请求获取信息失败,info == ‘ . var_export( $info, true ) );
            }
            return false;
        }

        //获取用户资料
        $uinfo       = $this->getUserinfoByOpenid( $info[‘openid‘] );
        if( !$uinfo )
            return false;
        /**
        if( !Config::get( ‘app_debug‘ ) ) {
            if( !isset( $uinfo[‘unionid‘] ) || empty( $uinfo[‘unionid‘] ) ) {
                $this->myLog( ‘用户基本资料中没有得到unionid, info=== ‘ . var_export( $info, true ) );
                return false;
            }
        }else {
            //测试服,自己生成一个
            if( !isset( $uinfo[‘unionid‘] ) || empty( $uinfo[‘unionid‘] ) ) {
                $uinfo[‘unionid‘]       = ‘__‘ . md5( $info[‘openid‘] . Config::get(‘wx.fuwuhaoWxchatlxdTest‘) );
            }
        }
        */

        if( !isset( $uinfo[‘unionid‘] ) ) {
            $uinfo[‘unionid‘]       = ‘__‘ . md5( $info[‘openid‘] . Config::get(‘wx.fuwuhaoWxchatlxdTest‘) );
            if( !Config::get( ‘app_debug‘ ) ) {
                $uinfo[‘unionid‘]       = ‘__‘ . md5( $info[‘openid‘] . Config::get(‘wx.fuwuhaoWxchat‘) );
                $this->myLog( ‘用户基本资料中没有得到unionid, info=== ‘ . var_export( $info, true ) );
            }
        }

        $this->request->wxRegData       = [
            ‘user‘      => $uinfo,
            ‘param‘     => $this->request->wxRequest,
            ‘openid‘    => $info[‘openid‘],
        ];

        $model      = Loader::controller( ‘app\common\controller\User‘ );
        $info   = $model->getOneByUnionid();
        if( !$info ) {

            //注册一下。
            $res        = $model->addOneUserByWx();
            if( !$res ) {
                $this->myLog( ‘微信注册用户失败,‘ . $this->request->modelErr . ‘data == ‘ . json_encode( $this->request->wxRegData ) );
                return false;
            }

            $info   = $model->getOneByUnionid();
            if( !$info ) {
                $this->myLog( ‘通过unionid获取用户信息失败,info == ‘ . var_export( $info, true ) . ‘, wxrequest = ‘ . var_export( $this->request->wxRegData, true ) );
                return false;
            }
        }
        return $info;
    }

    /**
     * 验证访问来源是否来自于微信
     */
    public function checkWxIp()
    {
        $ip         = $this->request->ip(0, true);
        if( !$ip )
            return false;

        $ipList = $this->cache->get( $this->redisKey . ‘ip‘);
        if( !$ipList ) {
            $accessToken        = $this->getAccessToken();
            if( !$accessToken ) {
                $this->myLog( ‘获取微信token失败,请检查!!checkWxIp 方法执行异常‘ );
                return false;
            }

            $url    = Config::get(‘wx.apiUrl‘) . "/cgi-bin/getcallbackip?access_token=" . $accessToken;
            $info   = $this->sendCurl( $url );
            if( $info && isset( $info[‘ip_list‘] ) ) {
                $ipList         = $info[‘ip_list‘];
                if( !$this->cache->set( $this->redisKey . ‘ip‘, $info[‘ip_list‘], $this->ipListCt ) ){
                    $this->myLog( ‘缓存ip数据异常!‘ );
                    return false;
                }
            }
        }

        $this->myLog( ‘iplist ‘ . var_export( $ipList , true ) );
        if( !in_array( $ip, $ipList ) ) {
            $this->myLog( ‘非法访问,ip‘ . $ip . var_export( $_SERVER, true ) );
            return false;
        }
        return true;
    }

    /**
     * 菜单设置
     */
    public function setButton()
    {
        //获取用户信息
        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!setButton 方法执行异常‘ );
            return false;
        }

        $state          = Config::get( ‘wx.state‘ );
        $appid          = Config::get(‘wx.appID‘);
        $url            = ‘bm.mmd360.com‘;
        if( Config::get( ‘app_debug‘ ) ) {
            $appid      = Config::get( ‘wx.appIDlxdTest‘ );
            $url            = ‘ypdev.bimai.top‘;
        }

        $arr    = [
            ‘button‘    => [
                [
                    ‘name‘  => ‘自服务‘,
                    ‘sub_button‘    => [
                        [
                            ‘name‘  => ‘代理申请‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fuser%2FapplyAgency&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ],
                        [
                            ‘name‘  => ‘我的佣金‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fgain&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ],
                        [
                            ‘name‘  => ‘我的收藏‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fuser_collect%2FgetList&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ],
                        [
                            ‘name‘  => ‘使用帮助‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fuser%2Fhelp&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ],
                        [
                            ‘name‘  => ‘个人中心‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fuser&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ]
                    ]
                ],
                [
                    ‘name‘  => ‘商品库‘,
                    ‘sub_button‘    => [
                        [
                            ‘name‘  => ‘实时新品‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fgoods%2FgetTodayGoods&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ],
                        [
                            ‘name‘  => ‘必买推荐‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fgoods%2FhotCakeList&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ],
                        [
                            ‘name‘  => ‘热卖活动‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fgoods_activity%2FgetActivityList&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ],
                        [
                            ‘name‘  => ‘全部商品‘,
                            ‘type‘  => ‘view‘,
                            ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2Findex%2Fgoods_cate%2FgetCate&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                        ]
                    ]
                ],
//                [
//                    ‘name‘  => ‘邀请海报‘,
//                    ‘type‘  => ‘click‘,
//                    ‘key‘   => ‘invite‘,
//                ]
                [
                    ‘name‘  => ‘优品商城‘,
                    ‘type‘  => ‘view‘,
                    ‘url‘   => ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=http%3A%2F%2F‘.$url.‘%2F__youzan__&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘
                ]
            ]
        ];

        if( Config::get(‘app_debug‘) )
            $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/menu/create?access_token=‘ . $accessToken;
        else
            $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/menu/create?access_token=‘ . $accessToken;

        $info       = $this->sendCurl( $url, ‘post‘, json_encode( $arr, JSON_UNESCAPED_UNICODE ) );
        if( !$info ) {
            $this->myLog( ‘设置自定义菜单失败, url = ‘ . $url . ‘, info = ‘. var_export( $info, true ) );
            return true;
        }

        return true;
    }

    /**
     * 文本消息回复
     */
    public function eventText()
    {
        $this->request->wxRegData       = [
            ‘param‘     => $this->request->wxRequest,
            ‘openid‘    => $this->request->wxRequest[‘fuser‘]
        ];

        //验证消息是否为重复的,存在让微信不要再发了
        $model      = Loader::controller( ‘app\common\controller\Wxlog‘ );
        $res        = $model->isExiMsg();
        if( $res === false ) {
            $this->myLog( ‘查询msgid唯一性失败,reqeust = ‘ . $this->request->wxRequest );
            return false;
        }
        if( $res ) {
            echo ‘success‘;die;
        }

        $content        = $this->request->wxRequest[‘content‘];
        if( !$content ) {
            $this->myLog( ‘未获取到文本消息,reqeust = ‘ . $this->request->wxRequest );
            return false;
        }

        //验证是否存在 "找" 字
        $content            = trim( $content );
        if( $content == ‘签到‘ )
            $this->setTextMsgQd();

        if( mb_strpos( $content, ‘找‘, 0, ‘utf-8‘ ) !== false )
            $this->setTextMsgFind( $content );



        echo $this->responseText( $this->request->wxRequest[‘fuser‘], $this->request->wxRequest[‘tuser‘], ‘该业务尚未开通,请稍后再试~‘ );
        die;
    }


    /**
     * 文本回复方法 - 找
     */
    private function setTextMsgFind( $content )
    {
        if( mb_strpos( $content, ‘找‘, 0, ‘utf-8‘ ) === 0 ) {
            if( mb_strlen( $content, ‘utf-8‘) == 1 ) {
                echo $this->responseText( $this->request->wxRequest[‘fuser‘], $this->request->wxRequest[‘tuser‘], ‘您的输入有误,请输入【找?】,其中?为要查找的关键字,比如 ‘.PHP_EOL.‘找苹果‘ );
            }else {
                $keyword        = mb_substr( $content, 1 );
                $model          = Loader::controller( ‘app\index\controller\Search‘ );
                $info           = $model->api( $keyword );
                $recontent      = ‘‘;
                if( !$info || !isset( $info[1] ) ) {
                    $recontent  = <<<EOF
[发呆] [发呆] [发呆]  您搜索的【{$keyword}】 今天没有参加优惠活动,再看看其他的优惠商品~\n
大波优惠券商品向您袭来,猛戳链接??: http://t.cn/RQXc6yz\n
买不到不罢休的小伙伴,扭头看这里:【http://t.cn/RQXVS2p】
EOF;
                }else {
                    $url            = $this->makeUrlAndShort( $info[1] );
                    $recontent  = <<<EOF
为您查到:【{$keyword}】 的相关产品优惠:{$url}
EOF;
                }

                echo $this->responseText( $this->request->wxRequest[‘fuser‘], $this->request->wxRequest[‘tuser‘], $recontent);
            }
            die;
        }
    }

    /**
     * 文本回复方法 - 签到
     */
    private function setTextMsgQd()
    {
        $wxUinfo        = $this->getUserinfoByOpenid( $this->request->wxRequest[‘fuser‘] );
        if( !$wxUinfo ){
            $this->myLog( $this->request->wxRequest[‘fuser‘] . ‘通过微信获取用户信息失败, info=== ‘ . var_export( $this->request->wxRequest, true ) );
            $content        = <<<EOF
未获取到您的任何信息,请稍后再试![微笑] 
EOF;
            $this->_sendWXText( $content );
        }

        if( !isset( $wxUinfo[‘unionid‘] ) ) {
            $wxUinfo[‘unionid‘]       = ‘__‘ . md5( $this->request->wxRequest[‘fuser‘] . Config::get(‘wx.fuwuhaoWxchatlxdTest‘) );
            if( !Config::get( ‘app_debug‘ ) ) {
                $wxUinfo[‘unionid‘]       = ‘__‘ . md5( $this->request->wxRequest[‘fuser‘] . Config::get(‘wx.fuwuhaoWxchat‘) );
                $this->myLog( ‘用户基本资料中没有得到unionid, info=== ‘ . var_export( $this->request->wxRequest, true ) );
            }
        }

        $model          = Loader::controller( ‘app\common\controller\UserActivity‘ );
        $res            = $model->makeQD( $wxUinfo[‘unionid‘] );
        $str            = ‘签到成功‘;
        if( $res === false ) {
            $this->myLog( ‘签到方法失败,请检查 makeQD‘ . $this->request->modelErr );
            $content        = <<<EOF
服务器出错啦,请稍后再试![微笑]
EOF;
            $this->_sendWXText( $content );
        }

        if( $res == 1 )
            $str        = ‘您已经签到‘;
        else if( $res == 0 ) {
            $url            = $this->makeUrlAndShort( ‘/index/user/applyAgency‘ );
            $content        = <<<EOF
呀,您还不是代理!快点击进入传送门,成为代理,分享商品轻松赚钱
{$url}
EOF;
            $this->_sendWXText( $content );
        }


        $url            = $this->makeUrlAndShort( ‘/index/goods/hotCakeList‘ );
        $content        = <<<EOF
{$str},快去分享商品吧:
$url
EOF;
        $this->_sendWXText( $content );
    }

    /**
     * 返回短连接,并缓存
     */
    private function makeUrlAndShort( $url )
    {
        $cachename      = $this->redisKey . ‘makeUrlAndShort::‘ . $url;
        $info           = $this->cache->get( $cachename );
        if( $info )
            return $info;

        $state          = Config::get( ‘wx.state‘ );
        $appid          = Config::get(‘wx.appID‘);
        if( Config::get( ‘app_debug‘ ) )
            $appid      = Config::get( ‘wx.appIDlxdTest‘ );

        $uri            = urlencode( getHttpUrl() . $url );
        $longUrl        = ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=‘.$appid.‘&redirect_uri=‘.$uri.‘&response_type=code&scope=snsapi_base&state=‘.$state.‘#wechat_redirect‘;
        $info           = $this->long2short( $longUrl );
        if( $info ){
            $this->cache->set( $cachename, $info );
            return $info;
        }
        return $longUrl;
    }


    /**
     * 不支持的动作回复
     */
    public function noSupprt()
    {
        $content = <<<EOF
您好,相关业务暂无开通,请稍后再试~
EOF;

        echo $this->responseText( $this->request->wxRequest[‘fuser‘], $this->request->wxRequest[‘tuser‘], $content );
        die;
    }

    /**
     * 点击事件动作
     */
    public function eventClick()
    {
        $key        = isset( $this->request->wxRequest[‘eventkey‘] ) ? $this->request->wxRequest[‘eventkey‘] : ‘‘;
        if( !$key ) {
            $this->myLog( ‘微信:点击事件中未获取到key值!!‘ );
            $this->_sendWXText( ‘您好~‘ );
        }

        switch( $key ) {
            case ‘invite‘:
                $this->_clickInvite();
                break;
            default:
                $this->myLog( ‘微信:获取到未定义的key值,请检查!‘ );
                $content    = <<<EOF
哎呀~ 出故障啦,请稍后再试~ [微笑]
EOF;
                $this->_sendWXText( $content );
        }
    }

    /**
     * 邀请海报 按钮
     */
    protected function _clickInvite()
    {
        $fuser      = $this->request->wxRequest[‘fuser‘];
        register_shutdown_function( function() use ( $fuser ) {
            //获取用户信息
            $wxUinfo        = $this->getUserinfoByOpenid( $fuser );
            if( !$wxUinfo ){
                $this->myLog( $this->request->wxRequest[‘fuser‘] . ‘通过微信获取用户信息失败, info=== ‘ . var_export( $this->request->wxRequest, true ) );
                $this->kfResponseText( $fuser, ‘未获取到您的任何信息,请稍后再试![微笑] ‘ );
                return;
            }

            if( !isset( $wxUinfo[‘unionid‘] ) ) {
                $wxUinfo[‘unionid‘]       = ‘__‘ . md5( $this->request->wxRequest[‘fuser‘] . Config::get(‘wx.fuwuhaoWxchatlxdTest‘) );
                if( !Config::get( ‘app_debug‘ ) ) {
                    $wxUinfo[‘unionid‘]       = ‘__‘ . md5( $this->request->wxRequest[‘fuser‘] . Config::get(‘wx.fuwuhaoWxchat‘) );
                    $this->myLog( ‘用户基本资料中没有得到unionid, info=== ‘ . var_export( $this->request->wxRequest, true ) );
                }
            }

            $uid   = \app\common\controller\User::getUidByUninonid( $wxUinfo[‘unionid‘], true );
            if( $uid === 0 ) {
                $this->myLog( ‘通过微信获取必买用户信息失败, info=== ‘ . var_export( $this->request->wxRequest, true ) . ‘ resuid = ‘ . var_export( $uid, true ) );
                $helpurl        = $this->makeUrlAndShort( ‘/index/user/applyAgency‘ );
                $content    = <<<EOF
亲,先申请代理吧[微笑]
传送门:$helpurl
EOF;
                $this->kfResponseText( $fuser, $content );
                return;
            }
            if( !$uid ) {
                $this->myLog( ‘通过微信获取必买用户信息失败, info=== ‘ . var_export( $this->request->wxRequest, true ) . ‘ resuid = ‘ . var_export( $uid, true ) );
                $this->kfResponseText( $fuser, ‘未获取到您的任何信息,请稍后再试![微笑] ‘ );
                return;
            }

            $userModel      = Loader::model( ‘app\common\controller\User‘ );
            $userinfo       = $userModel->getInfoById( $uid );
            if( !isset( $userinfo[‘invite_img‘] ) || !$userinfo[‘invite_img‘] || !isset( $userinfo[‘head_pic‘]) )
                $userinfo   = $userModel->getInfoById( $uid, true );

            if( !$userinfo || !isset( $userinfo[‘invite_img‘] ) ) {
                $this->myLog( ‘通过用户id读取用户信息异常, $userinfo=== ‘ . var_export( $userinfo, true ) );
                $this->kfResponseText( $fuser, ‘未获取到您的任何信息,请稍后再试![微笑] ‘ );
                return;
            }

            if( $userinfo[‘invite_img‘] )
                $this->kfResponseImg( $fuser, Config::get( ‘upload.uploadpath‘ ) . $userinfo[‘invite_img‘], 100 );
            else {
                $res    = call_user_func(
                    [Loader::controller( ‘app\common\controller\User‘ ), ‘makeInvteImg‘],
                    [‘uid‘ => $uid, ‘pic‘ => $userinfo[‘head_pic‘] ]
                );
                if( isset( $res[‘error‘] ) ) {
                    $this->myLog( ‘生成用户海报失败‘ . $res[‘error‘] );
                    $content    = <<<EOF
很抱歉,生成海报失败,请稍后再试![微笑] 
EOF;

                    $this->kfResponseText( $fuser, $content );
                    return;
                }

                $this->_reMakeCurl();
                $this->kfResponseImg( $fuser, $res[‘path‘], 100 );
            }
        } );

        $content    = <<<EOF
正在为您生成专属邀请海报
请稍等...
EOF;
        $this->_sendWXText( $content );
    }

    /**
     * 点击菜单事件
     */
    public function viewClickEvn()
    {

//        Chaoxin::sendTextMsg( ‘wx info ‘ . var_export($this->request->wxRequest, true ) );
//
//        echo $this->responseText( $this->request->wxRequest[‘fuser‘], $this->request->wxRequest[‘tuser‘], ‘你好!‘ );
//        die;
    }

    /**
     * 获取 jsapi_ticket
     */
    public function getJsapiTicket()
    {
        $cachename      = ‘js_access_token_online‘;
        if( Config::get( ‘app_debug‘ ) )
            $cachename      = ‘js_access_token‘;

        $jsaccessToken    = $this->cache->get( $this->redisKey . $cachename );
        if( !$jsaccessToken ) {
            //获取用户信息
            $accessToken        = $this->getAccessToken();
            if( !$accessToken ) {
                $this->myLog( ‘获取微信token失败,请检查!!getJsapiTicket 方法执行异常‘ . json_encode( func_get_args() ) );
                return false;
            }

            $url        = Config::get(‘wx.apiUrl‘) . "/cgi-bin/ticket/getticket?access_token={$accessToken}&type=jsapi";
            $res        = $this->sendCurl( $url );
            if( !isset( $res[‘ticket‘] ) || !$res[‘ticket‘] ) {
                $this->myLog( ‘获取 jstoken 失败,请检查‘ . var_export( $res, true ) );
                return false;
            }

            if( !$this->cache->set( $this->redisKey . $cachename, $res[‘ticket‘], $this->accessTokenCt ) ) {
                $this->myLog( ‘服务器缓存jstoken失败‘ );
                return false;
            }

            $jsaccessToken      = $res[‘ticket‘];
        }

        return $jsaccessToken;
    }

    /**
     * 获取 临时素材 图片
     */
    public function getMediaInfoPic( $media_id )
    {
        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!getJsapiTicket 方法执行异常‘ . json_encode( func_get_args() ) );
            return false;
        }

        $url        = Config::get(‘wx.apiUrl‘) . "/cgi-bin/media/get?access_token={$accessToken}&media_id={$media_id}";
        $source     = file_get_contents( $url );
        $imageinfo  = getimagesizefromstring( $source );
        $path       = Config::get( ‘upload.idcardpath‘ );
        if( !is_dir( $path ) ) {
            if( !mkdir( $path, 0755, true ) ) {
                $this->myLog( ‘ getMediaInfoPic 创建目录失败,‘ . __FILE__ );
                return false;
            }
        }
        $filename   = ‘‘;
        if( is_array( $imageinfo ) && isset( $imageinfo[‘mime‘] ) ) {
            switch ( $imageinfo[‘mime‘] ) {
                case ‘image/gif‘:
                    $filename   = $media_id . ‘.gif‘;
                    break;
                case ‘image/jpeg‘:
                    $filename   = $media_id . ‘.jpg‘;
                    break;
                case ‘image/png‘ :
                    $filename   = $media_id . ‘.png‘;
                    break;
                default :
                    $filename   = $media_id . ‘.jpg‘;
                    break;
            }

            if( !file_put_contents( $path . $filename, $source ) ) {
                return false;
            }

            return str_replace( ROOT_PATH . ‘public‘, ‘‘, $path ) . $filename;
        }
        return false;
    }

    /**
     * 新增临时素材
     * @param string $type  /image/voice/video/thumb
     *
     * @return string
     */
    public function setMediaInfo( $file, $type = ‘image‘ )
    {
        if( !is_file( $file ) ) {
            $this->myLog( ‘图片消息回复异常,图片查找失败‘ . var_export( is_file( $file ), true ), false );
            return false;
        }

        $fileMd5    = md5_file( $file );

        $cacheName  = ‘Wx:setMediaInfo:‘ . $fileMd5;
        $cacheInfo  = $this->cache->get( $cacheName );
        if( $cacheInfo )
            return $cacheInfo;

        //获取用户信息
        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!setButton 方法执行异常‘, false );
            return false;
        }

        $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/media/upload?access_token=‘.$accessToken.‘&type=‘ . $type;

        $fileData   = [
            ‘media‘     => "@{$file}"
        ];
        $info       = $this->sendCurl( $url, ‘post‘, $fileData, false );

        if( !$info ) {
            $this->myLog( ‘上传临时素材失败, url = ‘ . $url );
            return false;
        }

        if( isset( $info[‘media_id‘] ) && $info[‘media_id‘] ) {
            $this->cache->set( $cacheName, $info[‘media_id‘], 252000 ); //70小时
            return $info[‘media_id‘];
        }

        return false;
    }

    /**
     * 文本消息回复
     * @param $toUser
     * @param $fromUser
     * @param $content
     * @return string
     */
    public function responseText( $toUser, $fromUser, $content ){
        $textTpl = <<<EOF
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>
EOF;
        $resultStr = sprintf($textTpl, $toUser, $fromUser, time(), $content );
        return $resultStr;
    }

    /**
     * 图片消息回复
     * @param string $mediaid  绝对路径
     * @return string
     */
    public function responseImg( $toUser, $fromUser, $imgpath )
    {
        //上传临时素材
        $mediaid    = $this->setMediaInfo( $imgpath );
        if( !$mediaid ) {
            $this->myLog( ‘responseImg 上传临时素材失败,返回为空,imgpath ‘ . $imgpath );
            $this->_sendWXText( ‘很抱歉,服务器临时出现故障,请稍后再试...‘ );
        }

        $textTpl    = <<<EOF
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image><MediaId><![CDATA[%s]]></MediaId>
</Image>
</xml>
EOF;
        $resultStr = sprintf($textTpl, $toUser, $fromUser, time(), $mediaid );
        return $resultStr;
    }

    /**
     * 客服消息 图片消息
     * @param int $usleep 是否等待几毫秒,生成海报操作中避免比文字出现的时间早,
     *
     * @return boolean
     */
    public function kfResponseImg( $openid, $imgpath, $usleep = 0 )
    {
        if( $usleep )
            usleep( $usleep );

        //上传临时素材
        $mediaid    = $this->setMediaInfo( $imgpath );
        if( !$mediaid ) {
            $this->myLog( ‘responseImg 上传临时素材失败,返回为空,imgpath ‘ . $imgpath , false );
            return false;
        }

        $postData     = [
            ‘touser‘    => $openid,
            ‘msgtype‘   => ‘image‘,
            ‘image‘     => [
                ‘media_id‘  => $mediaid
            ],
        ];

        //获取用户信息
        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!setButton 方法执行异常‘, false );
            return false;
        }

        $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/message/custom/send?access_token=‘.$accessToken;
        $info       = $this->sendCurl( $url, ‘post‘, $postData );
        if( !$info || $info[‘errcode‘] ) {
            $this->myLog( ‘发送客服图片消息失败, url = ‘ . $url . ‘ pd == ‘ . json_encode( $postData ), false );
            return true;
        }

        return true;
    }

    /**
     * 客服消息 文字消息
     *
     * @return boolean
     */
    public function kfResponseText( $openid, $content )
    {

        $postData     = [
            ‘touser‘    => $openid,
            ‘msgtype‘   => ‘text‘,
            ‘text‘     => [
                ‘content‘  => $content
            ],
        ];

        //获取用户信息
        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!setButton 方法执行异常‘, false );
            return false;
        }

        $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/message/custom/send?access_token=‘.$accessToken;
        $info       = $this->sendCurl( $url, ‘post‘, json_encode( $postData, JSON_UNESCAPED_UNICODE) );
        if( !$info || $info[‘errcode‘] ) {
            $this->myLog( ‘发送客服文本消息失败, url = ‘ . $url . ‘ pd == ‘ . json_encode( $postData ), false );
            return true;
        }

        return true;
    }


    /**
     * 生成微信xml
     * @param string $encrypt 加密后的消息密文
     * @param string $signature 安全签名
     * @param string $timestamp 时间戳
     * @param string $nonce 随机字符串
     *
     * @return string
     */
    public static function createXml( $encrypt, $signature, $timestamp, $nonce )
    {
        $format = "<xml>
<Encrypt><![CDATA[%s]]></Encrypt>
<MsgSignature><![CDATA[%s]]></MsgSignature>
<TimeStamp>%s</TimeStamp>
<Nonce><![CDATA[%s]]></Nonce>
</xml>";
        return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
    }

    /**
     * 日志
     */
    public function __destruct()
    {
    }

    /**
     * 发送模板消息
     */
    public function sendTemplateMsg( $data )
    {
        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!eventSubscribe 方法执行异常‘ . json_encode( func_get_args() ) );
            return false;
        }

        $url    = Config::get( ‘wx.apiUrl‘ ) . ‘/cgi-bin/message/template/send?access_token=‘. $accessToken;
        $this->sendCurl( $url, ‘post‘, $data );
    }

    /**
     * 微信服务号 长连接转短连接
     */
    public function long2short( $longurl )
    {
        if( !$longurl )
            return ‘‘;

        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!eventSubscribe 方法执行异常‘ . json_encode( func_get_args() ) );
            return false;
        }

        if( Config::get(‘app_debug‘) )
            $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/shorturl?access_token=‘ . $accessToken;
        else
            $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/shorturl?access_token=‘ . $accessToken;

        $res    =  $this->sendCurl( $url, ‘post‘, [
            ‘action‘        => ‘long2short‘,
            ‘long_url‘      => $longurl
        ] );

        if( !isset( $res[‘errcode‘] ) || $res[‘errcode‘] != 0 ){
            $this->myLog( ‘长连接转短连接失败‘ . json_encode( $res ) );
            return false;
        }

        return $res[‘short_url‘];
    }

    /**
     * 文本消息回复终端
     */
    private function _sendWXText( $content )
    {
        ob_start();
        echo $this->responseText( $this->request->wxRequest[‘fuser‘], $this->request->wxRequest[‘tuser‘], $content );
        ob_end_flush();
    }

    /**
     * 图片消息回复终端
     * @param string $file 绝对路径
     */
    private function _sendWXImg( $file )
    {
        ob_start();
        echo $this->responseImg( $this->request->wxRequest[‘fuser‘], $this->request->wxRequest[‘tuser‘], $file );
        ob_end_flush();
    }

    /**
     * 生成永久二维码的ticket
     * @param string $scene_str 最大64位
     */
    public function getQcodeTicket( $scene_str )
    {
        $len        = strlen( $scene_str );
        if( $len < 1 || $len > 64 ) {
            $this->myLog( ‘生成永久二维码的ticket scenstr 长度不合法,, len = ‘ . $len );
            return false;
        }

        $accessToken        = $this->getAccessToken();
        if( !$accessToken ) {
            $this->myLog( ‘获取微信token失败,请检查!!eventSubscribe 方法执行异常‘ . json_encode( func_get_args() ) );
            return false;
        }
        $url        = Config::get(‘wx.apiUrl‘) . ‘/cgi-bin/qrcode/create?access_token=‘ . $accessToken;
        $res    =  $this->sendCurl( $url, ‘post‘, [
            ‘action_name‘   => ‘QR_LIMIT_STR_SCENE‘,
            ‘action_info‘   => [
                ‘scene‘     => [
                    ‘scene_str‘     => $scene_str
                ]
            ],
        ] );
        if( !isset( $res[‘ticket‘] ) || !$res[‘ticket‘] ) {
            $this->myLog( ‘生成永久二维码的ticket 失败,res == ‘ . var_export( $res, true ) );
            return false;
        }

        return $res[‘ticket‘];
    }

    /**
     * 读取二维码图片
     * @param string $response 返回值
     *
     * @return string url
     */
    public function getQcodeImg( $ticket )
    {
        return ‘https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=‘ . $ticket;
    }

    /**
     * 已关注扫描二维码
     *
     */
    public function eventScan()
    {
        $content = <<<EOF
赶快分享吧~[奸笑][奸笑][奸笑]
EOF;
        $this->_sendWXText( $content );
    }

    public function _reMakeCurl()
    {
        $this->curl->close();
        $this->curl     = null;
        $this->curl     = new Curl();
    }

    /**
     * 发送模板消息
     */
    protected function _sendWxTemplate( $template_id, $touser, $url, $data )
    {
        $data[‘template_id‘]        = $template_id;
        if( !$data[‘template_id‘] )
            return ;

        $data[‘touser‘]         = $touser;
        if( !$data[‘touser‘] )
            return ;

        $http       = ‘http://‘;
        if( $_SERVER[‘SERVER_PORT‘] == 443 )
            $http   = ‘https://‘;

        $data[‘url‘]            = $http . $_SERVER[‘SERVER_NAME‘] . $url;
        $data[‘data‘]           = $data;

        register_shutdown_function( function() use ($data ) {
            $wx         = Wx::instance();
            return  $wx->sendTemplateMsg( $data );
        } );
    }
}

  

以上是关于微信 服务号开发的主要内容,如果未能解决你的问题,请参考以下文章

微信小程序代码片段

微信订阅号开发-1

Python微信公众号开发

微信公众号开发

微信公众号如何使用域名直接调试本地开发环境代码的方案

微信公众号开发-遇到的坑