<?php
namespace Payment;
/**
 * User: liuyuzhen
 * Date: 2018/7/5
 * Time: 11:27
 * Description:
 */
class TencentPay {

    protected $sslcert_path ='/application/library/Payment/cert/qq/apiclient_cert.pem';
    protected $sslkey_path = '/application/library/Payment/cert/qq/apiclient_key.pem';
    protected $appid = '';
    protected $pay_url = "https://qpay.qq.com/cgi-bin/pay/qpay_unified_order.cgi";
    protected $curl_timeout = 30;
    protected $notify_url = '';
    protected $response = '';
    protected $result ='';
    protected $key = '';
    protected $mch_id ='';
    protected $s_appid ='';
    protected $s_key = '';
    protected $s_mch_id ='';
    protected $prepay_id ='';
    protected $tradeType = '';
    public  $data;//接收到的数据，类型为关联数组
    var $returnParameters;//返回参数，类型为关联数组
    var $parameters;//请求参数，类型为关联数组

    public function setRequestParams($orderInfo){
        $pay_sn = $orderInfo['paySn'];
        $total_fee = $orderInfo['orderAmount'];
        $this->parameters['appid']=$this->appid;
        $this->parameters['mch_id']=$this->mch_id;
        $this->parameters['nonce_str']=\Our\CommonExtension::createNoncestr(32);
        //$this->parameters['body']='订单号：'.implode(',',$orderInfo['orderSns']);
        $this->parameters['body']=$orderInfo['storeName'].'的订单';
        $this->parameters['attach']='r|'.$pay_sn;
        $this->parameters['out_trade_no']=$pay_sn;
        $this->parameters['fee_type']= \Our\NameConst::currentCNY;
        if(APP_ENV == 'develop'||APP_ENV == 'test'||APP_ENV == 'pre'){
            $this->parameters['total_fee'] = 1;
        }else{
            $this->parameters['total_fee'] = $total_fee;
        }
        if(isset($orderInfo['openid'])&&$orderInfo['openid']){
            $this->parameters['openid'] = $orderInfo['openid'];
        }
        $this->parameters['spbill_create_ip']=\Our\Common::getClientIp();
        $this->parameters['notify_url']=$this->notify_url;
        $this->parameters['trade_type']= $this->tradeType;
        //$this->parameters['openid']=$_SESSION['openid'];
        //print_r($this->parameters);exit;
        $prepay_id = $this->getPrepayId();
        $this->setPrepayId($prepay_id);
    }

    public function setServiceRequestParams($orderInfo){
        $this->key = $this->s_key;
        $pay_sn = $orderInfo['paySn'];
        $total_fee = $orderInfo['orderAmount'];
        $this->parameters['appid']=$this->s_appid;
        $this->parameters['mch_id']=$this->s_mch_id;
        $this->parameters['sub_appid']=$this->appid;
        $this->parameters['sub_mch_id']=$orderInfo['subMchId'];
        $this->parameters['nonce_str']=\Our\CommonExtension::createNoncestr(32);
        //$this->parameters['body']='订单号：'.implode(',',$orderInfo['orderSns']);
        $this->parameters['body']=$orderInfo['storeName'].'的订单';
        $this->parameters['attach']='r|'.$pay_sn;
        $this->parameters['out_trade_no']=$pay_sn;
        $this->parameters['fee_type']= \Our\NameConst::currentCNY;
        if(APP_ENV == 'develop'||APP_ENV == 'test'||APP_ENV == 'pre'){
            $this->parameters['total_fee'] = 1;
        }else{
            $this->parameters['total_fee'] = $total_fee;
        }
        if(isset($orderInfo['openid'])&&$orderInfo['openid']){
            $this->parameters['sub_openid'] = $orderInfo['openid'];
        }
        $this->parameters['spbill_create_ip']=\Our\Common::getClientIp();
        $this->parameters['notify_url']=$this->notify_url;
        $this->parameters['trade_type']= $this->tradeType;
        //$this->parameters['openid']=$_SESSION['openid'];
        //print_r($this->parameters);exit;
        $prepay_id = $this->getPrepayId();
        $this->setPrepayId($prepay_id);
    }

    /**
     *  作用：设置prepay_id
     */
    function setPrepayId($prepayId)
    {
        $this->prepay_id = $prepayId;
    }


    /**
     * 获取prepay_id
     */
    function getPrepayId()
    {
        $this->postXml();
        $this->result = $this->xmlToArray($this->response);
        $prepay_id = isset($this->result["prepay_id"])?$this->result["prepay_id"]:\Our\ApiConst::zero;
        return $prepay_id;
    }

    /**
     *  作用：将xml转为array
     */
    public function xmlToArray($xml)
    {
        //将XML转为array
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }


    /**
     *  作用：post请求xml
     */
    function postXml()
    {
        $xml = $this->createXml();
        \Our\Log::getInstance()->write($xml,'/data/log/applog');
        $this->response = $this->postXmlCurl($xml,$this->pay_url,$this->curl_timeout);
        \Our\Log::getInstance()->write(serialize($this->response),'/data/log/applog');
        return $this->response;
    }

    /**
     *  作用：设置标配的请求参数，生成签名，生成接口参数xml
     */
    function createXml($signType='MD5')
    {
        if($signType=='MD5'){
            //echo '加签参数:'.json_encode($this->parameters)."\n";
            $this->parameters["sign"] = $this->getSign($this->parameters);//签名
        }else{
            $this->parameters["sign"] = $this->getHMACSHA1Sign($this->parameters,'sha256');
        }
        return  $this->arrayToXml($this->parameters);
    }

    /**
     * 	作用：array转xml
     */
    function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
            if (is_numeric($val))
            {
                $xml=$xml."<".$key.">".$val."</".$key.">";

            }
            else
                $xml=$xml."<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml=$xml."</xml>";
        return $xml;
    }


    /**
     *  作用：以post方式提交xml到对应的接口url
     */
    public function postXmlCurl($xml,$url,$second=30)
    {       //echo $xml,'-',$url,'-',$second;die;
        //初始化curl

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        curl_close($ch);
        //返回结果
        if($data)
        {   //var_dump($data);die('||');
            //curl_close($ch);
            return $data;
        }
        else
        {
            //$error = curl_errno($ch);
            //echo "curl出错，错误码:$error"."<br>";
            //echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            //curl_close($ch);
            //var_dump($error);die;
            return false;
        }
    }

    /**
     *  作用：使用证书，以post方式提交xml到对应的接口url
     */
    function postXmlSSLCurl($xml,$url,$second=30)
    {
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch,CURLOPT_HEADER,FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
        //设置证书
        //使用证书：cert 与 key 分别属于两个.pem文件
        //默认格式为PEM，可以注释
        curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLCERT, APPLICATION_PATH.$this->sslcert_path);
        //默认格式为PEM，可以注释
        curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLKEY, APPLICATION_PATH.$this->sslkey_path);
        //post提交方式
        curl_setopt($ch,CURLOPT_POST, true);
        curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
        $data = curl_exec($ch);
        //返回结果
        if($data){
            curl_close($ch);
            return $data;
        }
        else {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error"."<br>";
            echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            curl_close($ch);
            return false;
        }
    }



    /**
     *  作用：生成签名
     */
    public function getSign($Obj)
    {
        foreach ($Obj as $k => $v)
        {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);

        $String = $String."&key=".$this->key;
        $String = str_replace(array("\r\n", "\r", "\n"," "), "", $String);
        $String = md5($String);
        $result_ = strtoupper($String);
        return $result_;
    }


    /**
     *  作用：生成签名(按照HMAC-SHA1规则签名)
     */
    public function getHMACSHA1Sign($Obj,$signType ='sha1')
    {
        foreach ($Obj as $k => $v)
        {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //$String = $String."&key=".$this->key;
        //签名步骤三：HMAC-SHA1加密
        $String = hash_hmac($signType,$String,$this->key);
        $result_ = base64_encode($String);
        return $result_;
    }

    /**
     *  作用：格式化参数，签名过程需要使用
     */
    function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
            if($urlencode)
            {
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }
        $reqPar = '';
        if (strlen($buff) > 0)
        {
            $reqPar = substr($buff, 0, strlen($buff)-1);
        }
        return $reqPar;
    }

    function saveData($xml)
    {
        $this->data = $this->xmlToArray($xml);
    }

    function checkSign()
    {
        $tmpData = $this->data;
        unset($tmpData['sign']);
        $sign = $this->getSign($tmpData);//本地签名
        if ($this->data['sign'] == $sign) {
            return TRUE;
        }
        return FALSE;
    }

    /**
     * 获取notify信息
     */
    public function getNotifyInfo() {
        $xml = file_get_contents("php://input");
        $this->saveData($xml);
        $verify = false;
        if($this->checkSign() == FALSE){
            $this->setReturnParameter("return_msg","签名失败");//返回信息
        }else{
            $verify = true;
            $this->setReturnParameter("return_code","SUCCESS");//设置返回码
        }
        $returnXml = $this->returnXml();
        if($verify) {
            return array(
                'out_trade_no' => $this->data['out_trade_no'],
                'trade_no' => $this->data['transaction_id'],
                'returnXml'=>$returnXml
            );
        }
        return false;
    }


    /**
     *  作用：产生随机字符串，不长于32位
     */
    public function createNoncestr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 将xml数据返回微信
     */
    function returnXml()
    {
        //$returnXml = $this->createXml();
        //return $returnXml;

        $arr = $this->returnParameters;

        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
            if (is_numeric($val))
            {
                $xml.="<".$key.">".$val."</".$key.">";

            }
            else
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml.="</xml>";
        return $xml;

    }


    /**
     * 设置返回微信的xml数据
     */
    function setReturnParameter($parameter, $parameterValue)
    {
        $this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
    }

    private function trimString($value)
    {
        $ret = null;
        if (null != $value)
        {
            $ret = $value;
            if (strlen($ret) == 0)
            {
                $ret = null;
            }
        }
        return $ret;
    }


}