<?php

namespace App\Http\Middleware;

use App\Services\Common\CurlService;
use App\Wechat\WechatParam;
use Closure;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

/**
 * 公众号检查,获取支付openid
 * Class OfficialCheck
 * @package App\Http\Middleware
 * Auth Zhong
 * Date 2019/2/22
 */
class WechatCheck
{

    /**
     * OfficialCheck constructor.
     */
    public function __construct()
    {
    }


    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        //获取jsapi_ticket
        $jsapi_ticket = Cache::get('wechat_jsapi_ticket');
        if (empty($jsapi_ticket)) {
            $jsapi_ticket = $this->_getJsApiTicket();
            if (empty($jsapi_ticket)) {
                return $next($request);
            }
        }

        //生成配置
        $timestamp = time();
        $noncestr  = $this->_generateNonceStr();
        $url       = $request->getUri();
        $ret       = strpos($url, '#');
        if ($ret) {
            $url = substr($url, 0, $ret);
        }
        $url = trim($url);
        if (empty($url))
            return $next($request);
        $arrdata = ["timestamp" => $timestamp, "noncestr" => $noncestr, "url" => $url, "jsapi_ticket" => $jsapi_ticket];
        $sign    = $this->_getSignature($arrdata);
        if (!$sign)
            return $next($request);
        WechatParam::instance()->merge([
            "nonceStr"  => $noncestr,
            "timestamp" => $timestamp,
            "url"       => $url,
            "signature" => $sign,
        ]);

        return $next($request);
    }

    /**
     * 获取jsapi_ticket
     */
    private function _getJsApiTicket()
    {
        $access_token = $this->_getAccessToken();
        if (empty($access_token)) {
            return null;
        }

        $curl = new CurlService();
        $res  = $curl->get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' . $access_token . '&type=jsapi');
        if (!empty($res)) {
            if (isset($res->ticket)) {
                Cache::put("wechat_jsapi_ticket", $res->ticket, 100);
                return $res->ticket;
            }
        }

        return null;
    }

    /**
     * 获取access_token
     */
    private function _getAccessToken()
    {
        $access_token = Cache::get('wechat_access_token');
        if (empty($access_token)) {
            $curl   = new CurlService();
            $wechat = WechatParam::instance()->getParam();
            $res    = $curl->get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$wechat['appid']}&secret={$wechat['appsecret']}");
            if (!empty($res)) {
//                Log::info('获取token:' . json_encode($res));
                if (isset($res->access_token)) {
                    Cache::put("wechat_access_token", $res->access_token, 100);
                    return $res->access_token;
                }
            }

            return null;
        }

        return $access_token;
    }

    /**
     * 生成随机字串
     * @param number $length 长度,默认为16,最长为32字节
     * @return string
     */
    private function _generateNonceStr($length = 16)
    {
        // 密码字符集,可任意添加你需要的字符
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str   = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $str;
    }

    /**
     * 获取签名
     * @param array $arrdata 签名数组
     * @param string $method 签名方法
     * @return boolean|string 签名值
     */
    private function _getSignature($arrdata, $method = "sha1")
    {
        if (!function_exists($method)) return false;
        ksort($arrdata);
        $paramstring = "";
        foreach ($arrdata as $key => $value) {
            if (strlen($paramstring) == 0)
                $paramstring .= $key . "=" . $value;
            else
                $paramstring .= "&" . $key . "=" . $value;
        }
        $Sign = $method($paramstring);
        return $Sign;
    }
}