<?php

/**

 * 微信支付

*/

namespace Common\Vendor\Pay\Driver;

class Weixinapppay extends \Common\Vendor\Pay\Pay {

    protected $gateway = 'https://mapi.alipay.com/gateway.do';

    protected $verify_url = 'http://notify.alipay.com/trade/notify_query.do';

    protected $parameters;

    public $config = array(

        'SSLCERT_PATH' => 'cacert/apiclient_cert.pem',

        'SSLKEY_PATH' => 'cacert/apiclient_key.pem',

        'CURL_TIMEOUT' => '60',

        'CURL_PROXY_HOST' => '0.0.0.0',

        'CURL_PROXY_PORT' => '0',

        'REPORT_LEVENL' => '1',

    );

    public function check() {

        if (!$this->config['config_account'] || !$this->config['config_partner'] || !$this->config['config_key'] || !$this->config['config_other']) {

            E("参数有误！");

        }return true;

    }

    public function buildRequestForm(\Common\Vendor\Pay\PayVo $vo) {

        $body=$vo->getBody();if(empty($body))$body='微信APP支付';

        $money=$vo->getFee();

        $this->parameters=array(

            'body'=>$body,

            'out_trade_no'=>$vo->getOrderNo(),

            'total_fee'=>($money*100),

            'notify_url'=>$this->config['notify_url'],

            'trade_type'=>"APP",

            'spbill_create_ip'=>get_client_ip(),

        );

        $this->prepay_id=$this->getPrepayId();

        $jsApiParameters = $this->getParameters();

        return $jsApiParameters;

    }

    public function get_pay_code($info=''){}

    /**

     * 针对notify_url验证消息是否是发出的合法消息

     * @return 验证结果

     */

    public function verifyNotify($notify) {

        //生成签名结果

        $notify=$this->xmlToArray($notify);

        $isSign = $this->getSignVeryfy($notify, $notify["sign"]);

        if ($isSign) {

        $this->setInfo($notify);return true;}else {return false;}

    }

    /**

     * 获取返回时的签名验证结果

     * @param $para_temp 通知返回来的参数数组

     * @param $sign 返回的签名结果

     * @return 签名验证结果

     */    

    protected function getSignVeryfy($param, $sign) {

        unset($param['sign']);

        $mysgin =$this->getSign($param);

        if ($mysgin == $sign){return true;}else{

            $result['return_code']='FAIL';$result['return_msg']='签名失败';

            echo $this->arrayToXml($result);exit;}

    }

    public function notifySuccess(){

        $result['return_code']='SUCCESS';

        $result['return_msg']='OK';

        echo $this->arrayToXml($result);exit;

    }

    protected function setInfo($notify) {

        $info = array();

        //支付状态

        $info['status'] = true;

        $info['money'] = $notify['total_fee']/100;

        $info['out_trade_no'] = $notify['out_trade_no'];

        $info['pay_sn'] = $notify['transaction_id'];

        $info['openid'] = $notify['openid']; // 获取用户与APP商户唯一对应的id

        $this->info = $info;

    }

	/**

	* 	作用：设置jsapi的参数

	*/

	public function getParameters(){	   

       $nonceStr=$this->getNonceStr();

	   $timeStamp = time();

	   $params=array(

            'appid'=>$this->config['config_account'],

            'partnerid'=>$this->config['config_partner'],

            'prepayid'=>$this->prepay_id,

            'package'=>'Sign=WXPay',

            'noncestr'=>$nonceStr,

            'timestamp'=>"$timeStamp",

       );

		$jsParamObj["apiKey"]    =  $params['appid'];           //请求生成支付签名时需要,js调起支付参数中不需要

		$jsParamObj["orderId"]   =  $params['prepayid'];

		$jsParamObj["mchId"]     =  $params['partnerid'];//商家和微信合作的 id 号

        $jsParamObj["nonceStr"]  =  $params['noncestr'];

		$jsParamObj["timeStamp"] =  $params['timestamp'];      //用大写的timeStamp参数请求生成支付签名	

		$jsParamObj["package"]   =  $params['package'];

		$jsParamObj["sign"] = $this->getSign($params);

		return $jsParamObj;

	}

	/**

	* 获取prepay_id

	*/

	function getPrepayId(){

		$result = $this->xmlToArray($this->postXml());

		$prepay_id = $result["prepay_id"];

		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();//trace('wechat：postXml'.$xml,'微信','DEBUG',true);

		return  $this->postXmlCurl($xml,"https://api.mch.weixin.qq.com/pay/unifiedorder",false,$this->config['CURL_TIMEOUT']);

	}

  	/**

	 * 

	 * 产生随机字符串，不长于32位

	 * @param int $length

	 * @return 产生的随机字符串

	 */

	public static function getNonceStr($length = 32) 

	{

		$chars = "abcdefghijklmnopqrstuvwxyz0123456789";$str ="";

		for ( $i = 0; $i < $length; $i++ )  {  

			$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  

		}return $str;

	}

	/**

	* 	作用：以post方式提交xml到对应的接口url

	*/

	public function postXmlCurl($xml,$url, $useCert = false,$second=30)

	{

		//初始化curl

		$ch = curl_init();

		//设置超时

		curl_setopt($ch, CURLOPT_TIMEOUT, ($this->CURL_TIMEOUT?$this->CURL_TIMEOUT:$second));

		//这里设置代理，如果有的话

		if($this->config['CURL_PROXY_HOST'] != "0.0.0.0" && $this->config['CURL_PROXY_PORT'] != 0){

			curl_setopt($ch,CURLOPT_PROXY, $this->config['CURL_PROXY_HOST']);

			curl_setopt($ch,CURLOPT_PROXYPORT, $this->config['CURL_PROXY_PORT']);

		}

		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);    

		if($useCert == true){//设置证书

			//使用证书：cert 与 key 分别属于两个.pem文件

			curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');

			curl_setopt($ch,CURLOPT_SSLCERT, $this->config['SSLCERT_PATH']);

			curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');

			curl_setopt($ch,CURLOPT_SSLKEY, $this->config['SSLKEY_PATH']);

		}

		//post提交方式

		curl_setopt($ch, CURLOPT_POST, TRUE);

		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

		//运行curl

		$data = curl_exec($ch);

		curl_close($ch);

		//返回结果

		if($data)

		{

		  curl_close($ch);

		  return $data;

		}

		else

		{

		  $error = curl_errno($ch);

		  echo "curl出错，错误码:$error"."<br>";

		  curl_close($ch);

		  return false;

		}

	}

	/**

	* 	作用：设置标配的请求参数，生成签名，生成接口参数xml

	*/

	function createXml()

	{

		$this->parameters["appid"] = $this->config['config_account'];//公众账号ID

		$this->parameters["mch_id"] = $this->config['config_partner'];//店铺号

		$this->parameters["nonce_str"] = $this->getNonceStr();//随机字符串

		$this->parameters["sign"] = $this->getSign($this->parameters);//签名

		return  $this->arrayToXml($this->parameters);

	}

	/**

	* 	作用：array转xml

	*/

	function arrayToXml($arr){

		$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;

	}

	/**

	* 	作用：格式化参数，签名过程需要使用

	*/

	private 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;

	}

	/**

	* 	作用：生成签名

	*/

	public function getSign($Obj)

	{

		foreach ($Obj as $k => $v)

		{

		  $Parameters[$k] = $v;

		}

		//签名步骤一：按字典序排序参数

		ksort($Parameters);

		$String = $this->formatBizQueryParaMap($Parameters, false);

		//echo '【string1】'.$String.'</br>';

		//签名步骤二：在string后加入KEY

		$String = $String."&key=".$this->config['config_key'];

		//echo "【string2】".$String."</br>";

		//签名步骤三：MD5加密

		$String = md5($String);

		//echo "【string3】 ".$String."</br>";

		//签名步骤四：所有字符转为大写

		$result_ = strtoupper($String);

		//echo "【result】 ".$result_."</br>";

		return $result_;

	}

    public function refund($out_trade_no,$trade_no,$refund_amount,$refund_sn){

        $res=array('status'=>0,'msg'=>'');

		$order_total_price = idtoname($trade_no,'OrderInfo','pay_amount','pay_sn');

		$parameters=array(

            'out_trade_no'=>$out_trade_no,  //订单号

            'out_refund_no'=>$refund_sn,	//退款单号

            'total_fee'=>($order_total_price*100),  //订单金额

            'refund_fee'=>($refund_amount*100),	//退款金额

            

        );//print_r($parameters);exit;

        $url = "https://api.mch.weixin.qq.com/secapi/pay/refund";		

        $response = $this->createXmls($parameters);

        $result = $this->getResult($response);

		if($result['result_code'] == 'SUCCESS'){

			$payment_refund = idtoname($refund_sn,'PaymentRefund','*','refund_sn');

            $status =1;

            D('Common/OrderInfo')->payment_refund($refund_sn,$status);



            $info['status'] = true;

            $info['money'] = $payment_refund['money'];

            $info['out_trade_no'] = $payment_refund['order_id'];

            $info['pay_sn'] = $payment_refund['trade_no'];

            D('Common/OrderInfo')->refund_order($info);

            $res['status'] = 1;

			$res['msg'] = '退款成功';

		}else{

		   $res['msg'] = $result['err_code_des'].'('.$result['err_code'].')';

		}

		return $res;



    }



    public function createXmls($input)

	{

		

			//检测必填参数

			if(!$input['out_trade_no']) {

				E("缺少统一支付接口必填参数out_trade_no！");

			}else if(!$input['out_refund_no']) {

				E("缺少统一支付接口必填参数out_refund_no！");

			}else if(!$input['total_fee']) {

				E("缺少统一支付接口必填参数total_fee！");

			}else if(!$input['refund_fee']) {

				E("缺少统一支付接口必填参数refund_fee！");

			}

			$appId = $this->config['config_account'];

		   	$input["appid"]=$appId;//公众账号ID

			$input["mch_id"] = $this->config['config_partner'];//店铺号



			//$input["spbill_create_ip"]=$_SERVER['REMOTE_ADDR'];//终端ip	  

			//$inputObj->SetSpbill_create_ip("1.1.1.1");  	    

			$input["nonce_str"] = $this->getNonceStr();//随机字符串	



			$input["out_refund_no"] = $input['out_refund_no'];

			$input["out_trade_no"] =$input['out_trade_no'];

			$input["refund_fee"] = $input['refund_fee'];

			$input["total_fee"] = $input['total_fee'];

		   // $input["refund_account"] = 'REFUND_SOURCE_RECHARGE_FUNDS';

			$input["transaction_id"] = '';	



			//签名	

			$input["sign"] = $this->getSigns($input);//签名

			if($this->getSignVeryfys($input,$input["sign"]) == true){



				$xml = $this->arrayToXmls($input);	



				return $xml;

			}

	}

	public function ToUrlParams($arr){

		$buff = "";

        	ksort($arr);

		foreach ($arr as $k => $v)

		{

			if($k != "sign" && $v != "" && !is_array($v)){

				$buff .= $k . "=" . $v . "&";

			}

		}

		

		$buff = trim($buff, "&");

		return $buff;

	}

	public function getSigns($Obj)	{

		foreach ($Obj as $k => $v)

		{

		  $Parameters[$k] = $v;

		}

		//签名步骤一：按字典序排序参数

		ksort($Parameters);



		$String = $this->ToUrlParams($Parameters, false);

		//echo '【string1】'.$String.'</br>';

		//签名步骤二：在string后加入KEY

		$String = $String."&key=".$this->config['config_key'];

		//echo "【string2】".$String."</br>";

		//签名步骤三：MD5加密

		$String = md5($String);

		//echo "【string3】 ".$String."</br>";

		//签名步骤四：所有字符转为大写

		$result_ = strtoupper($String);

		//echo "【result】 ".$result_."</br>";

		return $result_;

	}

	

	 function postXmlSSL($response)

	{	

		

		$url = "https://api.mch.weixin.qq.com/secapi/pay/refund";

		$res = $this->postXmlCurls($response,$url,true,30);

		return $res;

	}

	

	

	function getResult($response)	{		



		$res = $this->postXmlSSL($response);

		$result = $this->xmlToArray($res);

        $this->getSigns($result);

		return $result;

	}

	function arrayToXmls($arr)

    {

        $xml = "<xml>";

        foreach ($arr as $key=>$val)

        {

        	

        	 	$xml.="<".$key.">".$val."</".$key.">"; 

        	 

        }

        $xml.="</xml>";

        return $xml; 

    }

    /**

	 * 以post方式提交xml到对应的接口url

	 * 

	 * @param string $xml  需要post的xml数据

	 * @param string $url  url

	 * @param bool $useCert 是否需要证书，默认不需要

	 * @param int $second   url执行超时时间，默认30s

	 * @throws WxPayException

	 */

	private function postXmlCurls($xml, $url, $useCert = false, $second = 30){

		$ch = curl_init();//初始化一个cURL会话		

		//设置超时

		curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置一个cURL传输选项   设置cURL允许执行的最长秒数。

		

		//如果有配置代理这里就设置代理

		if($this->config['CURL_PROXY_HOST'] != "0.0.0.0" 

			&& $this->config['CURL_PROXY_HOST'] != 0){

			curl_setopt($ch,CURLOPT_PROXY, $this->config['CURL_PROXY_HOST']);

			curl_setopt($ch,CURLOPT_PROXYPORT, $this->config['CURL_PROXY_HOST']);

		}

		curl_setopt($ch,CURLOPT_URL, $url);//需要获取的URL地址，也可以在curl_init()函数中设置。



		//curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);

		//curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验



		if(stripos($url,"https://")!==FALSE){

            curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);

            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

        }    else    {

            curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);

            curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验

		} 

		//设置header

		curl_setopt($ch, CURLOPT_HEADER, FALSE);

		//要求结果为字符串且输出到屏幕上

		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);



		if($useCert == true){

			//设置证书

			//使用证书：cert 与 key 分别属于两个.pem文件

			curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');

			curl_setopt($ch,CURLOPT_SSLCERT, ROOT_PATH.'/Application/Common/Vendor/Pay/Driver/Weixin/cert/apiclient_cert.pem');

			curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');

			curl_setopt($ch,CURLOPT_SSLKEY, ROOT_PATH.'/Application/Common/Vendor/Pay/Driver/Weixin/cert/apiclient_key.pem');

		}

		//post提交方式

		curl_setopt($ch, CURLOPT_POST, TRUE);

		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);



		//运行curl

		$data = curl_exec($ch);

		 

		//返回结果

		if($data){

			curl_close($ch);

			return $data;

		} else { 

			$error = curl_errno($ch);

			curl_close($ch);

			E("curl出错，错误码:$error");

		}

	}

	protected function getSignVeryfys($param, $sign) {

        unset($param['sign']);

        $mysgin =$this->getSigns($param);

        if ($mysgin == $sign){return true;}else{

            $result['return_code']='FAIL';$result['return_msg']='签名失败';

            echo $this->arrayToXmls($result);exit;}

    }

}

?>