<?php


namespace App\Servers;


use App\Models\ErrorRecord;
use App\Servers\Weixin\Unifiedorder;
use Illuminate\Support\Facades\Cache;
use function Couchbase\defaultDecoder;

class WeixinServer
{
    static private $server = null;
    public $mch_id = '';
    public $private_key = '';
    public $cert_file = '';
    public $key_file = '';

    public $appid = '';
    public $appsecret = '';

    public $appletId = '';
    public $appletKey = '';

    function __construct()
    {
        $this->mch_id=env('WX_MCH_ID','');
        $this->private_key=env('WX_PRIVATE_KEY','');
        $this->appid=env('WX_APPID','');
        $this->appsecret=env('WX_APPSECRET','');
        $this->cert_file=env('WX_CERT_FILE','');
        $this->key_file=env('WX_KEY_FILE','');
        $this->appletId=env('APPLET_ID','');
        $this->appletKey=env('APPLET_KEY','');
    }


    /**
     * 创建对象
     * @return WeixinServer
     */
    static function creatServer()
    {
        if (empty(self::$server)) self::$server = new WeixinServer();
        return self::$server;
    }

    /**
     * 发送模板消息
     * @param $data
     */
    function sendTemplate($data)
    {
        $access_token = $this->getAccessToken();
        $url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' . $access_token;
        $result = $this->sendRequest($url, 'post', $data);
        $result = json_decode($result, true);
        if ($result['errcode'] == 0) {
            //消息发放成功

        } else {
            //消息发放失败
            ErrorRecord::create(['m_id' => 0, 'msg' => '模板消息推送失败', 'data' => json_encode($data)]);
            ErrorRecord::create(['m_id' => 0, 'msg' => '模板消息推送失败', 'data' => json_encode($result)]);
        }
        return $result;
    }

    /**
     * 根据openid获取会员信息
     * @param $openid
     * @return false|mixed
     */
    function getUserInfo($openid)
    {
        $access_token = $this->getAccessToken();
        $url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' . $access_token . '&openid=' . $openid . '&lang=zh_CN';
        $result = $this->sendRequest($url, 'get');
        $result = json_decode($result, true);
        if (empty($result['openid'])) {
            //消息发放成功
            return $result;
        } else {
            //消息发放失败
            ErrorRecord::create(['m_id' => 0, 'msg' => '接口获取会员用户信息失败', 'data' => $openid]);
            return false;
        }
    }


    /**
     * 获取会员微信细腻
     * @return false|\Illuminate\Contracts\Foundation\Application|\Illuminate\Session\SessionManager|\Illuminate\Session\Store|mixed
     */
    function getOpenId()
    {
        $openid = session('wx_openid');
        if ($openid) return $openid;
        $code = request()->input('code', '');
        if (!isset($code)) {
            $redirect_url = $this->getServerUrl();
            $redirect_url = urlencode($redirect_url);
            $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appid}&redirect_uri={$redirect_url}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
            redirect($url);
            return false;
        } else {
            $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appid}&secret={$this->appsecret}&code={$code}&grant_type=authorization_code";
            $result = $this->sendRequest($url);
            $result = json_decode($result, true);
            if (isset($result['errcode'])) {
                return false;
            } else {
                $openid = $result['openid'];
                $url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={$this->appid}&grant_type=refresh_token&refresh_token={$result['refresh_token']} ";
                $result = $this->sendRequest($url);
                $result = json_decode($result, true);
                if (isset($result['access_token'])) {
                    return $result['openid'];
                }
                session(['wx_openid' => $openid]);
                return $openid;
            }
        }
    }

    /**
     * 小程序code获取openid等信息
     * @param $code
     * @param $invite_code
     * @return array|false
     */
    function getAppletsOpenId($code){

        $url = "https://api.weixin.qq.com/sns/jscode2session?appid={$this->appletId}&secret={$this->appletKey}&js_code={$code}&grant_type=authorization_code";
        $result = $this->sendRequest($url);
        $result = json_decode($result, true);

        if ( !isset($result['openid'])) {//成功返回信息
            return ['code'=>0,'msg'=>$result['errmsg']];
        } else {
            return $result;
        }
    }

    /**
     * 网页授权
     * @return mixed
     */
    function authorize()
    {
        $code = request()->input('code', '');
        if (empty($code)) {
            $redirect_url = $this->getServerUrl();
            $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appid}&redirect_uri={$redirect_url}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
            $this->redirect($url);
            return false;
        } else {
            $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appid}&secret={$this->appsecret}&code={$code}&grant_type=authorization_code";
            $result = $this->sendRequest($url);
            $result = json_decode($result, true);
            if (isset($result['errcode'])) {
                return false;
            } else {
                $openid = $result['openid'];
                $access_token = $result['access_token'];
                $url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={$this->appid}&grant_type=refresh_token&refresh_token={$result['refresh_token']} ";
                $result = $this->sendRequest($url);
                $result = json_decode($result, true);
                if (isset($result['access_token'])) {
                    $access_token = $result['access_token'];
                    $openid = $result['openid'];
                }
                $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$access_token}&openid={$openid}&lang=zh_CN";
                $result = $this->sendRequest($url);
                $result = json_decode($result, true);
                if (isset($result['errcode'])) {
                    return false;
                }
                return $result;
            }
        }

    }

    function redirect($uri = '', $method = 'location', $http_response_code = 302)
    {
        if (!preg_match('#^https?://#i', $uri)) {
            $uri = site_url($uri);
        }
        switch ($method) {
            case 'refresh'    :
                header("Refresh:0;url=" . $uri);
                break;
            default            :
                header("Location: " . $uri, TRUE, $http_response_code);
                break;
        }
        exit();
    }

    /**
     * 获取小程序码
     * @param $page  //页面路径
     * @param $scene  //携带参数
     */
    function getAppletCode($page,$scene){
        $access_token = $this->getAccessToken(2);

        $url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={$access_token}";
        $data['page'] = $page;
        $data['scene'] = $scene;
        $data['check_path'] = false;
        $data['width'] = 300;
        $result = $this->sendRequest($url,'post',$data);

        return $result;
    }

    /**
     * 获取 access_token
     * @return bool
     */
    protected function getAccessToken($type = 1)
    {
        if($type == 1){
            $key = 'vouchername';
            $appId = $this->appid;
            $appsecret = $this->appsecret;
        }else{
            $key = 'applet_voucher';
            $appId = $this->appletId;
            $appsecret = $this->appletKey;
        }
        $data = RedisDataServer::creatServer()->getData($key, 'json');
        if (empty($data) || $data['endtime'] < time()) {
            $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appId}&secret={$appsecret}";
            $result = $this->sendRequest($url);
            $result = json_decode($result, true);
//            $bey = Cache::set('vouchername', array('access_token' => $result['access_token'], 'endtime' => time() + 7000), 7000);
            if ( isset($result['access_token'])) {
                RedisDataServer::creatServer()->setData($key, array('access_token' => $result['access_token'], 'endtime' => time() + 1800), 'json',1800);
                return $result['access_token'];
            } else {
                return false;
            }
        } else {
            return $data['access_token'];
        }
    }

    /**
     * 获取js授权信息
     * @return mixed
     */
    function getJsConfig($url = '')
    {
        $hash = '';
        $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz';
        $max = strlen($chars) - 1;
        for ($i = 0; $i < 16; $i++) {
            $hash .= $chars[mt_rand(0, $max)];
        }
        $data['noncestr'] = $hash;
        $data['jsapi_ticket'] = $this->getJsTicket();
        $data['timestamp'] = time();
        if (empty($url)) {
            $data['url'] = $this->getServerUrl();
        } else {
            $data['url'] = $url;
        }
        ksort($data);
        $str = '';
        foreach ($data as $key => $val) {
            $str .= '&';
            $str .= $key . '=' . $val;
        }
        $str = mb_substr($str, 1);
        $data['appid'] = $this->appid;
        $data['signature'] = sha1($str);
        unset($data['jsapi_ticket']);
        return $data;
    }

    /**
     * 获取当前连接
     * @return string
     */
    function getServerUrl()
    {
//        $http_host=request()->header('host');
//        $data=request()->all();
        return urlencode('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
//        return 'http://'.$http_host. '?' . http_build_query($data);;
    }

    /**
     * 获取Js调用凭证
     * @return bool
     */
    function getJsTicket()
    {
//        $data = Cache::get('js_ticket');
        $data = RedisDataServer::creatServer()->getData('js_ticket', 'json');
        if (empty($data) || $data['endtime'] < time()) {
            $access_token = $this->getAccessToken();
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$access_token}&type=jsapi";
            $result = $this->sendRequest($url);
            $result = json_decode($result, true);
            if ($result['errcode'] == 0) {
                //file_put_contents($this->js_ticket,serialize(array('access_token'=>$result['access_token'],'endtime'=>time()+7000)));
//                Cache::set('js_ticket', array('access_token' => $result['ticket'], 'endtime' => time() + 7000), 7000);
                RedisDataServer::creatServer()->setData('js_ticket', array('access_token' => $result['ticket'], 'endtime' => time() + 7000), 'json',7000);
                return $result['ticket'];
            }
            return false;
        } else {
            return $data['access_token'];
        }
    }


    /**
     * 微信签名
     */
    function wxSign($data, $keys)
    {
        ksort($data);
        $str = '';
        foreach ($data as $key => $val) {
            if ($val) {
                if ($str != '') $str .= '&';
                $str .= "{$key}={$val}";
            }
        }
        $str .= '&key=' . $keys;
        $sign = strtoupper(MD5($str));
        return $sign;
    }


    /**
     * @param $url
     * @param string $type 请求方式
     * @param string $data 数据 数组格式
     * @return mixed
     */
    protected function sendRequest($url, $type = 'get', $data = '')
    {

        $ch = curl_init();
        if ($type == 'get' && $data) {
            $url = $url . '?' . http_build_query($data);
        }
        curl_setopt($ch, CURLOPT_URL, $url); //设置访问路径
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //设置可以返回字符串
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $head = array('User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36');
        curl_setopt($ch, CURLOPT_HTTPHEADER, $head);
        if ($type == 'post') {
            curl_setopt($ch, CURLOPT_POST, TRUE);//post请求
            $data = json_encode($data, JSON_UNESCAPED_UNICODE);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);//设置传递的参数
        }
        $request = curl_exec($ch);
        curl_close($ch);
        return $request;
    }

    /**
     * JS支付签名
     * @param $order_sn
     * @param $total_fee
     * @param $notify_url
     * @param $body
     * @param $wx_openid
     * @return bool
     */
    public function wxJsPay($order_sn, $total_fee, $notify_url, $body, $wx_openid = '')
    {
        $is_test=env('TEST_SERVE',false);
        if($is_test)$total_fee = 0.01;
        $conf = array(
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
            'notify_url' => $notify_url,
            'openid' => $wx_openid,
            'out_trade_no' => $order_sn,
            'total_fee' => $total_fee * 100,
            'trade_type' => 'JSAPI',
            'body' => $body,

        );

        $order = new Unifiedorder($conf);
        $request = $order->getOrder($this->private_key);
        if ($request['code'] == 1) {
            $datas["appId"] = $request['data']['appid'];
            $datas["nonceStr"] = $order->getNonceStr();
            $datas["package"] = "prepay_id={$request['data']['prepay_id']}";
            $datas["signType"] = 'MD5';
            $datas["timeStamp"] = time();
            $s = $order->wxSign($datas, $this->private_key);
            $datas["paySign"] = $s;
            return $datas;
        } else {
            return false;
        }
    }

    /**
     * JS支付签名
     * @param $order_sn
     * @param $total_fee
     * @param $notify_url
     * @param $body
     * @param $wx_openid
     * @return bool
     */
    public function getPayOrder($order_sn)
    {
        $is_test=env('TEST_SERVE',false);
        if($is_test)$total_fee = 0.01;
        $cert_file=base_path($this->cert_file);
        $key_file=base_path($this->key_file);
        $conf = array(
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
            'out_trade_no' => $order_sn,

        );
        $order = new Unifiedorder($conf, ['cert_file' => $cert_file, 'key_file' => $key_file]);
        $request = $order->getPayOrder($this->private_key);
        return $request;

    }


    /**
     * 微信验签
     * @param $data
     * @return string
     */
    function verifyCer($data)
    {
        if (isset($data['sign'])) unset($data['sign']);
        $conf = array(
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
        );
        $order = new Unifiedorder($conf);
        $sign = $order->wxSign($data, $this->private_key);
        return $sign;
    }

    /**
     * 微信退款
     * @param $total_fee
     * @param $out_trade_no
     * @param string $body
     * @param int $order_money
     * @return array
     */

    function wxPayRefund($out_trade_no, $total_fee,  $body = '',$order_money=0)
    {
        $is_test=env('TEST_SERVE',false);
        if($is_test){
            $total_fee = 0.01;
            $order_money = 0.01;
        }
        $conf = array(
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
            'out_trade_no' => $out_trade_no,
            'out_refund_no' => 'T'.$out_trade_no.time(),
            'total_fee' => $order_money * 100,
            'refund_fee' => $total_fee * 100,
            'refund_desc' => $body
        );
        $order = new Unifiedorder($conf, ['cert_file' => base_path($this->cert_file), 'key_file' =>  base_path($this->key_file)]);
        $request = $order->wxPayRefund($this->private_key);
        ErrorRecord::create(['m_id' =>0, 'msg' => '微信退款失败', 'data' => json_encode($request)]);
        if ($request['code'] == 1) {
            return ['code' => 1, 'msg' => '退款成功', 'data' => ['ret_sn' => $request['data']['refund_id']]];
        } else {

            return ['code' => 0, 'msg' => '微信退款失败', 'data' => ['ret_sn' => '']];
        }
    }

    /**
     * 微信付款到零钱
     * @param $order_sn
     * @param $total_fee
     * @param $openid
     * @param $body
     * @return array
     */
    function setVerified($order_sn, $total_fee, $openid, $body)
    {
        $cert_file=base_path($this->cert_file);
        $key_file=base_path($this->key_file);
        $private_key=$this->private_key;
        $conf = array(
            'mch_appid' => $this->appid,
            'mchid' =>  $this->mch_id,
            'openid' => $openid,
            'partner_trade_no' => $order_sn,
            'amount' => $total_fee * 100,
            'check_name' => 'NO_CHECK',
            'desc' => $body,

        );
        $order = new Unifiedorder($conf, ['cert_file' => $cert_file, 'key_file' => $key_file]);
        $request = $order->setVerified($private_key);
        ErrorRecord::create(['m_id' =>0, 'msg' => '微信提现记录', 'data' => json_encode($request)]);
        return $request;
    }



}