<?php
/**
 * 支付宝无线支付
 * @Copyright (C) 2016 汉潮 All rights reserved.
 * @License http://www.hanchao9999.com
 * @Author xiaogg <xiaogg@sina.cn>
 */

namespace Common\Vendor\Pay\Driver;
class Alipayapi extends \Common\Vendor\Pay\Pay {
    protected $gateway = 'http://wappaygw.alipay.com/service/rest.htm?';
    protected $verify_url = 'http://notify.alipay.com/trade/notify_query.do';
    protected $config = array('email' => '','key' => '','partner' => '');
    public function check() {
        if (!$this->config['config_account'] || !$this->config['config_key'] || !$this->config['config_partner']) {
            E("支付宝无线设置有误！");
        }
        $this->config['sign_type']    = 'MD5';
        $this->config['input_charset']= 'utf-8';
        $this->config['cacert']    = getcwd().'\\cacert.pem';
        $this->config['transport']    = 'http';
        return true;
    }
    public function buildRequestForm(\Common\Vendor\Pay\PayVo $vo) {
        $format="xml";$v="2.0";$req_id=date('Ymdhis');
		//请求业务参数详细
        $req_data = '<direct_trade_create_req><notify_url>' . $this->config['notify_url'] . '</notify_url><call_back_url>' . $this->config['return_url']. '</call_back_url><seller_account_name>' . $this->config['config_account'] . '</seller_account_name><out_trade_no>' . $vo->getOrderNo() . '</out_trade_no><subject>' . $vo->getTitle() . '</subject><total_fee>' . $vo->getFee() . '</total_fee><merchant_url>' . $this->config['return_url'] . '</merchant_url></direct_trade_create_req>';
        $para_token = array(
    		"service" => "alipay.wap.trade.create.direct",
    		"partner" => trim($this->config['config_partner']),
    		"sec_id" => trim($this->config['sign_type']),
    		"format"	=> $format,//返回格式
    		"v"	=> $v,//返回格式
    		"req_id"	=> $req_id,//请求号
    		"req_data"	=> $req_data,
    		"_input_charset"	=> trim(strtolower($this->config['input_charset']))
        );
        $request_data=$this->buildRequestPara($para_token);
        $html_text=$this->getHttpResponsePOST($this->gateway, $this->config['cacert'],$request_data,$this->config['input_charset']);    
        $html_text = urldecode($html_text);//URLDECODE返回的信息
		// print_r($html_text);exit;
        $para_html_text = $this->parseResponse($html_text);//解析远程模拟提交后返回的信息
        $request_token = $para_html_text['request_token'];//获取request_token
        $req_data = '<auth_and_execute_req><request_token>' . $request_token . '</request_token></auth_and_execute_req>';
        $param = array(//构造要请求的参数数组，无需改动
        		"service" => "alipay.wap.auth.authAndExecute",
        		"partner" => trim($this->config['config_partner']),
        		"sec_id" => trim($this->config['sign_type']),
        		"format"	=> $format,
        		"v"	=> $v,
        		"req_id"	=> $req_id,
        		"req_data"	=> $req_data,
        		"_input_charset"	=> trim(strtolower($this->config['input_charset']))
        );
        ksort($param);reset($param);
        $arg = '';
        foreach ($param as $key => $value) {
            if ($value) {
                $arg .= "$key=$value&";
            }
        }
        $param['sign'] = md5(substr($arg, 0, -1) . $this->config['config_key']);
        $param['sign_type'] = 'MD5';
        $sHtml = $this->_buildForm($param, $this->gateway, 'get');
        return $sHtml;
    }
    public function get_pay_code($info='')
    {
        if(empty($info))$info=$this->config;
        if($info)
        {
            $html="<dl class='clearfix'>";
            if($info['logo']!='')
            $html.="<dt><img src='".$info['logo']."' /></dt>";
            $html.="<dd><input type='checkbox' name='paytype' value='".$info['id']."'>".$info['name']."</dd></dl>";
            return $html;
        }else{return '';}
    }
    /**
     * 获取返回时的签名验证结果
     * @param $para_temp 通知返回来的参数数组
     * @param $sign 返回的签名结果
     * @return 签名验证结果
     */
    protected function getSignVeryfy($param, $sign,$type='') {
        //除去待签名参数数组中的空值和签名参数
        $param_filter = array();
        while (list ($key, $val) = each($param)) {
            if ($key == "sign" || $key == "sign_type" || $val == "") {
                continue;
            } else {
                $param_filter[$key] = $param[$key];
            }
        }ksort($param_filter);reset($param_filter);
        //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = "";
        while (list ($key, $val) = each($param_filter)) {
            $prestr.=$key . "=" . $val . "&";
        }
        //去掉最后一个&字符
        $prestr = substr($prestr, 0, -1);
        $prestr = $prestr . $this->config['config_key'];
        $mysgin = md5($prestr);
        if($type)return $mysgin;
        if ($mysgin == $sign){return true;}else{return false;}
    }
    protected function getSignVeryfynotify($param, $sign) {
        //除去待签名参数数组中的空值和签名参数
        $prestr = 'service='. $param['service'];
        $prestr .= '&v='.$param['v'];
        $prestr .= '&sec_id='.$param['sec_id'];
        $prestr .= '&notify_data='.$param['notify_data'];
        $prestr .= $this->config['config_key'];
        $mysgin = md5($prestr);
        if ($mysgin == $sign){return true;}else{return false;}
    }
    /**
     * 针对notify_url验证消息是否是支付宝发出的合法消息
     * @return 验证结果
     */
    public function verifyNotify($notify) {
        //生成签名结果
        $isSign = $this->getSignVeryfynotify($notify, $notify["sign"]);
        //获取支付宝远程服务器ATN结果（验证是否是支付宝发来的消息）
        $responseTxt = 'true';
        if (!empty($notify["notify_id"])) {
            $responseTxt = $this->getResponse($notify["notify_id"]);
        }
		//$this->setInfo($notify);return true;
        if (preg_match("/true$/i", $responseTxt) && $isSign) {
        $this->setInfo($notify);return true;}else {return false;}
    }
    public function verifyNotifys($notify) {
        //生成签名结果
        $isSign = $this->getSignVeryfy($notify, $notify["sign"]);
        //获取支付宝远程服务器ATN结果（验证是否是支付宝发来的消息）
        $responseTxt = 'true';
        if (!empty($notify["notify_id"])) {
            $responseTxt = $this->getResponse($notify["notify_id"]);
        }
        if (preg_match("/true$/i", $responseTxt) && $isSign) {
        $this->setInfo($notify);return true;}else {return false;}
    }
    protected function setInfo($notify) {
        $notify_data=$this->getDataForXML($notify['notify_data']);
        $info = array();
        //支付状态
        $info['status'] = ($notify_data['trade_status'] == 'TRADE_FINISHED' || $notify_data['trade_status'] == 'TRADE_SUCCESS') ? true : false;
        $info['money'] = $notify_data['total_fee'];
        $info['out_trade_no'] = $notify_data['out_trade_no'];
        $info['pay_sn'] = $notify_data['trade_no'];
        $this->info = $info;
    }
    /**
     * 获取远程服务器ATN结果,验证返回URL
     * @param $notify_id 通知校验ID
     * @return 服务器ATN结果
     * 验证结果集：
     * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 
     * true 返回正确信息
     * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    protected function getResponse($notify_id) {
        $partner = $this->config['config_partner'];
        $veryfy_url = $this->verify_url . "?partner=" . $partner . "&notify_id=" . $notify_id;
        $responseTxt = $this->fsockOpen($veryfy_url);
        return $responseTxt;
    }
    /**
	 * 远程获取数据，POST模式
	 * 注意：
	 * 1.使用Crul需要修改服务器中php.ini文件的设置，找到php_curl.dll去掉前面的";"就行了
	 * 2.文件夹中cacert.pem是SSL证书请保证其路径有效，目前默认路径是：getcwd().'\\cacert.pem'
	 * @param $url 指定URL完整路径地址
	 * @param $cacert_url 指定当前工作目录绝对路径
	 * @param $para 请求的数据
	 * @param $input_charset 编码格式。默认值：空值
	 * return 远程输出的数据
	 */
    protected function getHttpResponsePOST($url, $cacert_url, $para, $input_charset = '') {
	if (trim($input_charset) != '') {
		$url = $url."_input_charset=".$input_charset;
	}
	$curl = curl_init($url);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
	curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
	curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//证书地址
	curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
	curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
	curl_setopt($curl,CURLOPT_POST,true); // post传输数据
	curl_setopt($curl,CURLOPT_POSTFIELDS,$para);// post传输数据
	$responseText = curl_exec($curl);
	//var_dump( curl_error($curl) );//如果执行curl过程中出现异常，可打开此开关，以便查看异常内容
	curl_close($curl);	
	return $responseText;
}
    protected function buildRequestPara($para_temp) {
        $para_temp['sign'] = $this->getSignVeryfy($para_temp,'','1');
        if($para_temp['service'] != 'alipay.wap.trade.create.direct' && $para_temp['service'] != 'alipay.wap.auth.authAndExecute') {
			$para_temp['sign_type'] = strtoupper(trim($this->alipay_config['sign_type']));
		}
    	return $para_temp;        
	}
    /**
     * 解析远程模拟提交后返回的信息
	 * @param $str_text 要解析的字符串
     * @return 解析结果
     */
    protected function parseResponse($str_text) {
		//以“&”字符切割字符串
		$para_split = explode('&',$str_text);
		//把切割后的字符串数组变成变量与数值组合的数组
		foreach ($para_split as $item) {
			//获得第一个=字符的位置
			$nPos = strpos($item,'=');
			//获得字符串长度
			$nLen = strlen($item);
			//获得变量名
			$key = substr($item,0,$nPos);
			//获得数值
			$value = substr($item,$nPos+1,$nLen-$nPos-1);
			//放入数组中
			$para_text[$key] = $value;
		}
		if( ! empty ($para_text['res_data'])) {
			//解析加密部分字符串
			if($this->config['sign_type'] == '0001') {
				$para_text['res_data'] = rsaDecrypt($para_text['res_data'], $this->config['private_key_path']);
			}
			//token从res_data中解析出来（也就是说res_data中已经包含token的内容）
            $doc = new \DOMDocument();
			$doc->loadXML($para_text['res_data']);
			$para_text['request_token'] = $doc->getElementsByTagName( "request_token" )->item(0)->nodeValue;
		}
		return $para_text;
	}
	protected function getDataForXML($res_data) {
        $xml = simplexml_load_string($res_data);
		$xml = (array)$xml;
        return $xml;
    }
    /**
     * @param  [type] $out_trade_no  商家订单号
     * @param  [type] $trade_no      支付宝交易号
     * @param  [type] $refund_amount 退款金额
     * @return [type] $refund_sn     退款单号
     */
    public function refund($out_trade_no,$trade_no,$refund_amount,$refund_sn){
        $refund_amount = floatval($refund_amount);
        if(empty($out_trade_no) || empty($trade_no) || empty($refund_amount))return false;
        $refund_content = 'tui';
        $ref_data = $trade_no."^".$refund_amount."^".$refund_content;
        $parameter = array(
            "service" =>"refund_fastpay_by_platform_pwd",
            "partner" => trim($this->config['config_partner']),
            "notify_url"=> str_replace("?s=",'',geturl('Home/Pay/refund_notify','',true,true)),
            'sign_type'=>'MD5',
            'seller_email'=>trim($this->config['config_account']),
            "refund_date"   => date("Y-m-d H:i:s",time()),
            "batch_no"  => $refund_sn,
            "batch_num" => 1,
            "detail_data"   => $ref_data,
            '_input_charset'=>'utf-8'
        );
        $alipaySubmit = new \Common\Vendor\Pay\Driver\Alipay\AlipayRefundSubmit ($this->config);
        $html_text = $alipaySubmit->buildRequestForm($parameter,"get", "确认");
        $res['status'] = 2;
        $res['data'] = $html_text;
        $res['submitname'] = 'alipaysubmit';
        return json_encode($res);
    }
}
?>