<?php
class Tenpay extends Pay{
	private $info;
	private $path;
	function __construct(){
		$path = str_replace("/Tenpay.class.php","/",str_replace("\\","/",__FILE__));
		$this->path = $path;
	}

	function pay($paymentId,$amount,$outTradeNo){
		$domain = $_SERVER['HTTP_HOST'];
		$payConfig = $this->getConfig($paymentId);
		$partner = $payConfig['partner'];
		$key = $payConfig['secret'];
		$return_url = 'http://'.$domain.'/pay/payReturn/tenpay';
		$notify_url = 'http://'.$domain.'/pay/payNotify/tenpay';
		$randNum = rand(1000, 9999);
		$strReq = $strTime . $randNum;	//10位序列号,可以自行调整。
		$sp_billno = $outTradeNo;	/* 商家订单号,长度若超过32位，取前32位。财付通只记录商家订单号，不保证唯一。 */
		$transaction_id = $bargainor_id . $strDate . $strReq;/* 财付通交易单号，10位商户号+8位时间（YYYYmmdd)+10位流水号*/
		$total_fee = $amount*100;	/* 商品价格（包含运费），以分为单位 */
		$desc = "订单号：" . $transaction_id;		/* 商品名称 */

$out_trade_no = $outTradeNo;	 /* 获取提交的订单号 */
$product_name = "商品";	/* 获取提交的商品名称 */
$order_price = $amount;	 /* 获取提交的商品价格 */
$remarkexplain = '';	/* 获取提交的备注信息 */
$trade_mode='1';/* 支付方式 */
$strDate = date("Ymd");
$strTime = date("His");
$total_fee = $order_price*100;	/* 商品价格（包含运费），以分为单位 */
$desc = $GLOBALS['siteConf']['siteName'];	/* 商品名称 */
$reqHandler = new RequestHandler();	/* 创建支付请求对象 */
$reqHandler->init();
$reqHandler->setKey($key);
$reqHandler->setGateUrl("https://gw.tenpay.com/gateway/pay.htm");
$reqHandler->setParameter("partner", $partner);
$reqHandler->setParameter("out_trade_no", $out_trade_no);
$reqHandler->setParameter("total_fee", $total_fee);  //总金额
$reqHandler->setParameter("return_url", $return_url);
$reqHandler->setParameter("notify_url", $notify_url);
$reqHandler->setParameter("body", $desc);
$reqHandler->setParameter("bank_type", "DEFAULT");  	  //银行类型，默认为财付通
$reqHandler->setParameter("spbill_create_ip", getIp());//客户端IP
$reqHandler->setParameter("fee_type", "1");               //币种
$reqHandler->setParameter("subject",$desc);          //商品名称，（中介交易时必填）
//系统可选参数
$reqHandler->setParameter("sign_type", "MD5");  	 	  //签名方式，默认为MD5，可选RSA
$reqHandler->setParameter("service_version", "1.0"); 	  //接口版本号
$reqHandler->setParameter("input_charset", "utf-8");   	  //字符集
$reqHandler->setParameter("sign_key_index", "1");    	  //密钥序号
//业务可选参数
$reqHandler->setParameter("attach", "");             	  //附件数据，原样返回就可以了
$reqHandler->setParameter("product_fee", "");        	  //商品费用
$reqHandler->setParameter("transport_fee", "0");      	  //物流费用
$reqHandler->setParameter("time_start", date("YmdHis"));  //订单生成时间
$reqHandler->setParameter("time_expire", "");             //订单失效时间
$reqHandler->setParameter("buyer_id", "");                //买方财付通帐号
$reqHandler->setParameter("goods_tag", "");               //商品标记
$reqHandler->setParameter("trade_mode",$trade_mode);              //交易模式（1.即时到帐模式，2.中介担保模式，3.后台选择（卖家进入支付中心列表选择））
$reqHandler->setParameter("transport_desc","");              //物流说明
$reqHandler->setParameter("trans_type","1");              //交易类型
$reqHandler->setParameter("agentid","");                  //平台ID
$reqHandler->setParameter("agent_type","");               //代理模式（0.无代理，1.表示卡易售模式，2.表示网店模式）
$reqHandler->setParameter("seller_id","");                //卖家的商户号
//请求的URL
$reqUrl = $reqHandler->getRequestURL();
//$debugInfo = $reqHandler->getDebugInfo();	
		echo "<script>setTimeout(function(){location.href='$reqUrl'},500)</script>";
		echo $form;
	}
	
	function payNotify(){
		global $db;
		$outTradeNo = getgpc("out_trade_no");
		if($outTradeNo){$log = '';foreach($_POST as $key=>$val){$log .= $key.'='.$val.'&';}if($log){saveLog('财付通通知数据：'.$log);}}
		$paymentId = $db->getOne("select paymentId from qk_onlinepay where outTradeNo='$outTradeNo'");
		$payConfig = $this->getConfig($paymentId);
		$key = $payConfig['secret'];
		$partner = $payConfig['partner'];
		$resHandler = new ResponseHandler();
		$resHandler->setKey($key);
		if($resHandler->isTenpaySign()) {
			$notify_id = $resHandler->getParameter("notify_id");
			$queryReq = new RequestHandler();
			$queryReq->init();
			$queryReq->setKey($key);
			$queryReq->setGateUrl("https://gw.tenpay.com/gateway/simpleverifynotifyid.xml");
			$queryReq->setParameter("partner", $partner);
			$queryReq->setParameter("notify_id", $notify_id);
			$httpClient = new TenpayHttpClient();
			$httpClient->setTimeOut(5);
			$httpClient->setReqContent($queryReq->getRequestURL());
			if($httpClient->call()) {
				$queryRes = new ClientResponseHandler();
				$queryRes->setContent($httpClient->getResContent());
				$queryRes->setKey($key);
				if($resHandler->getParameter("trade_mode") == "1"){
					if($queryRes->isTenpaySign() && $queryRes->getParameter("retcode") == "0" && $resHandler->getParameter("trade_state") == "0") {
						$out_trade_no = $resHandler->getParameter("out_trade_no");
						$tradeNo = $resHandler->getParameter("transaction_id");
						$payAmount = $resHandler->getParameter("total_fee");
						$discount = $resHandler->getParameter("discount");
						$this->payResult($outTradeNo,$payAmount/100,$remark,$tradeNo);
						echo "success";
					} else {
						$tmp = "即时到帐后台回调失败：验证签名失败 或 业务错误信息:trade_state=" . $resHandler->getParameter("trade_state") . ",retcode=" . $queryRes->                         getParameter("retcode"). ",retmsg=" . mb_convert_encoding($queryRes->getParameter("retmsg"),"UTF-8","GBK") . "<br/>" ;
					   saveLog($tmp);
						echo $tmp;
					   return false;
					}
				}elseif ($resHandler->getParameter("trade_mode") == "2"){
					if($queryRes->isTenpaySign() && $queryRes->getParameter("retcode") == "0" ) 
					{
							$out_trade_no = $resHandler->getParameter("out_trade_no");
							$transaction_id = $resHandler->getParameter("transaction_id");
							saveLog("中介担保后台回调，trade_state=".$resHandler->getParameter("trade_state"));
							switch ($resHandler->getParameter("trade_state")) {
								case "0":	//付款成功
								break;
								case "1":	//交易创建
									break;
								case "2":	//收获地址填写完毕
									break;
								case "4":	//卖家发货成功
									break;
								case "5":	//买家收货确认，交易成功
									break;
								case "6":	//交易关闭，未完成超时关闭
									break;
								case "7":	//修改交易价格成功
									break;
								case "8":	//买家发起退款
									break;
								case "9":	//退款成功
									break;
								case "10":	//退款关闭			
									break;
								default:
									break;
							}
							echo "success";
					} else {
						$tmp = "验证签名失败 或 业务错误信息:trade_state=" . $resHandler->getParameter("trade_state") . ",retcode=" . $queryRes->             										       getParameter("retcode"). ",retmsg=" . $queryRes->getParameter("retmsg") . "<br/>" ;
						   saveLog("中介担保后台回调失败：".$tmp);
						   return false;
					}
				}
				$tmp = "http res:" . $httpClient->getResponseCode() . "," . $httpClient->getErrInfo() . " ";
				$tmp .= "query req:" . htmlentities($queryReq->getRequestURL(), ENT_NOQUOTES, "GB2312") . " ";
				$tmp .= "query res:" . htmlentities($queryRes->getContent(), ENT_NOQUOTES, "GB2312") . " ";
				$tmp .= "query reqdebug:" . $queryReq->getDebugInfo() . " " ;
				$tmp .= "query resdebug:" . $queryRes->getDebugInfo() . " ";
				saveLog($tmp);
			}else{
					$tmp= "call err:" . $httpClient->getResponseCode() ."," . $httpClient->getErrInfo() . "";
					return false;
			 }
	
		}else {
			$tmp = "认证签名失败：".$resHandler->getDebugInfo() . "";
			echo $tmp;
			saveLog($tmp);
		}
		return false;
	}

	function payReturn(){
		global $db;
		$out_trade_no = getgpc("out_trade_no");
		$paymentId = $db->getOne("select paymentId from qk_onlinepay where outTradeNo='$out_trade_no'");
		$payConfig = $this->getConfig($paymentId);
		$key = $payConfig['secret'];
		$resHandler = new ResponseHandler();
		$resHandler->setKey($key);
		if($resHandler->isTenpaySign()) {
			$notify_id = $resHandler->getParameter("notify_id");
			$out_trade_no = $resHandler->getParameter("out_trade_no");
			$transaction_id = $resHandler->getParameter("transaction_id");
			$total_fee = $resHandler->getParameter("total_fee");
			$discount = $resHandler->getParameter("discount");
			$trade_state = $resHandler->getParameter("trade_state");
			$trade_mode = $resHandler->getParameter("trade_mode");
			if("1" == $trade_mode ) {
				if( "0" == $trade_state){
					//echo '支付成功';exit;
					return true;
				} else {
					return false;
				}
			}elseif( "2" == $trade_mode  ) {
				if( "0" == $trade_state) {
					$tmp = "中介担保支付成功";
				} else {
					$tmp = "中介担保支付失败";
				}
			}
		} else {
			$tmp = "前台认证签名失败：".$resHandler->getDebugInfo();
		}
		saveLog($tmp);
		return false;
	}
}

function  log_result($word) {
    $fp = fopen("log.txt","a");
    flock($fp, LOCK_EX) ;
    fwrite($fp,"执行日期：".strftime("%Y%m%d%H%M%S",time())."\n".$word."\n\n");
    flock($fp, LOCK_UN);
    fclose($fp);
}

class RequestHandler {
	var $gateUrl;
	var $key;
	var $parameters;
	var $debugInfo;
	function __construct() {$this->RequestHandler();}
	function RequestHandler() {
		$this->gateUrl = "https://www.tenpay.com/cgi-bin/v1.0/service_gate.cgi";
		$this->key = "";
		$this->parameters = array();
		$this->debugInfo = "";
	}
	function init() {}
	function getGateURL() {return $this->gateUrl;}
	function setGateURL($gateUrl) {$this->gateUrl = $gateUrl;}
	function getKey() {return $this->key;}
	function setKey($key) {$this->key = $key;}
	function getParameter($parameter) {return $this->parameters[$parameter];}
	function setParameter($parameter, $parameterValue) {$this->parameters[$parameter] = $parameterValue;}
	function getAllParameters() {return $this->parameters;}
	function getRequestURL() {
		$this->createSign();
		$reqPar = "";
		ksort($this->parameters);
		foreach($this->parameters as $k => $v) {
			$v=urlencode($v);
			//str_replace(".","%2E",$);
			$reqPar .= $k . "=" . $v . "&";
		}
		$reqPar = substr($reqPar, 0, strlen($reqPar)-1);
		$requestURL = $this->getGateURL() . "?" . $reqPar;
		return $requestURL;
	}
	function getDebugInfo() {
		return $this->debugInfo;
	}
	function doSend() {
		header("Location:" . $this->getRequestURL());
		exit;
	}
	function createSign() {
		$signPars = "";
		ksort($this->parameters);
		foreach($this->parameters as $k => $v) {
			if("" != $v && "sign" != $k) {
				$signPars .= $k . "=" . $v . "&";
			}
		}
		$signPars .= "key=" . $this->getKey();
		$sign = strtolower(md5($signPars));
		$this->setParameter("sign", $sign);
		$this->_setDebugInfo($signPars . " => sign:" . $sign);
	}	
	function _setDebugInfo($debugInfo) {
		$this->debugInfo = $debugInfo;
	}
}
class ResponseHandler  {
	var $key;
	var $parameters;
	var $debugInfo;
	function __construct() {
		$this->ResponseHandler();
	}
	function ResponseHandler() {
		$this->key = "";
		$this->parameters = array();
		$this->debugInfo = "";
		unset($_GET['PATH_INFO']);
		unset($_GET['a']);
		unset($_GET['m']);
		foreach($_GET as $k => $v) {
			$this->setParameter($k, $v);
		}
		foreach($_POST as $k => $v) {
			$this->setParameter($k, $v);
		}
	}
	function getKey() {
		return $this->key;
	}
	function setKey($key) {
		$this->key = $key;
	}
	function getParameter($parameter) {
		return $this->parameters[$parameter];
	}
	function setParameter($parameter, $parameterValue) {
		$this->parameters[$parameter] = $parameterValue;
	}
	function getAllParameters() {
		return $this->parameters;
	}	
	function isTenpaySign() {
		$signPars = "";
		ksort($this->parameters);
		//print_r($this->parameters);
		foreach($this->parameters as $k => $v) {
			if("sign" != $k && "" != $v) {
				$signPars .= $k . "=" . $v . "&";
			}
		}
		$signPars .= "key=" . $this->getKey();
		$sign = strtolower(md5($signPars));
		$tenpaySign = strtolower($this->getParameter("sign"));
		$this->_setDebugInfo($signPars . " => sign:" . $sign .
				" tenpaySign:" . $this->getParameter("sign"));
		return $sign == $tenpaySign;
	}
	function getDebugInfo() {return $this->debugInfo;}
	function doShow($show_url) {
		$strHtml = "<html><head>\r\n" .
			"<meta name=\"TENCENT_ONLINE_PAYMENT\" content=\"China TENCENT\">" .
			"<script language=\"javascript\">\r\n" .
				"window.location.href='" . $show_url . "';\r\n" .
			"</script>\r\n" .
			"</head><body></body></html>";
			
		echo $strHtml;
		
		exit;
	}
	function _isTenpaySign($signParameterArray) {
		$signPars = "";
		foreach($signParameterArray as $k) {
			$v = $this->getParameter($k);
			if("sign" != $k && "" != $v) {
				$signPars .= $k . "=" . $v . "&";
			}			
		}
		$signPars .= "key=" . $this->getKey();
		$sign = strtolower(md5($signPars));
		$tenpaySign = strtolower($this->getParameter("sign"));
		$this->_setDebugInfo($signPars . " => sign:" . $sign .
				" tenpaySign:" . $this->getParameter("sign"));
		return $sign == $tenpaySign;		
	}
	function _setDebugInfo($debugInfo) {
		$this->debugInfo = $debugInfo;
	}
}


?><?php

/**
 * http、https通信类
 * ============================================================================
 * api说明：
 * setReqContent($reqContent),设置请求内容，无论post和get，都用get方式提供
 * getResContent(), 获取应答内容
 * setMethod($method),设置请求方法,post或者get
 * getErrInfo(),获取错误信息
 * setCertInfo($certFile, $certPasswd, $certType="PEM"),设置证书，双向https时需要使用
 * setCaInfo($caFile), 设置CA，格式未pem，不设置则不检查
 * setTimeOut($timeOut)， 设置超时时间，单位秒
 * getResponseCode(), 取返回的http状态码
 * call(),真正调用接口
 * 
 * ============================================================================
 *
 */

class TenpayHttpClient {
	//请求内容，无论post和get，都用get方式提供
	var $reqContent;
	//应答内容
	var $resContent;
	//请求方法
	var $method;
	
	//证书文件
	var $certFile;
	//证书密码
	var $certPasswd;
	//证书类型PEM
	var	$certType;
	
	//CA文件
	var $caFile;
	
	//错误信息
	var $errInfo;
	
	//超时时间
	var $timeOut;
	
	//http状态码
	var $responseCode;
	
	function __construct() {
		$this->TenpayHttpClient();
	}
	
	
	function TenpayHttpClient() {
		$this->reqContent = "";
		$this->resContent = "";
		$this->method = "post";

		$this->certFile = "";
		$this->certPasswd = "";
		$this->certType = "PEM";
		
		$this->caFile = "";
		
		$this->errInfo = "";
		
		$this->timeOut = 120;
		
		$this->responseCode = 0;
		
	}
	
	
	//设置请求内容
	function setReqContent($reqContent) {
		$this->reqContent = $reqContent;
	}
	
	//获取结果内容
	function getResContent() {
		return $this->resContent;
	}
	
	//设置请求方法post或者get	
	function setMethod($method) {
		$this->method = $method;
	}
	
	//获取错误信息
	function getErrInfo() {
		return $this->errInfo;
	}
	
	//设置证书信息
	function setCertInfo($certFile, $certPasswd, $certType="PEM") {
		$this->certFile = $certFile;
		$this->certPasswd = $certPasswd;
		$this->certType = $certType;
	}
	
	//设置Ca
	function setCaInfo($caFile) {
		$this->caFile = $caFile;
	}
	
	//设置超时时间,单位秒
	function setTimeOut($timeOut) {
		$this->timeOut = $timeOut;
	}
	
	//执行http调用
	function call() {
		//启动一个CURL会话
		$ch = curl_init();

		// 设置curl允许执行的最长秒数
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeOut);

		// 获取的信息以文件流的形式返回，而不是直接输出。
		curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);

		// 从证书中检查SSL加密算法是否存在
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
				
		
		$arr = explode("?", $this->reqContent);
		if(count($arr) >= 2 && $this->method == "post") {
			//发送一个常规的POST请求，类型为：application/x-www-form-urlencoded，就像表单提交的一样。
			curl_setopt($ch, CURLOPT_POST, 1);
			curl_setopt($ch, CURLOPT_URL, $arr[0]);
			//要传送的所有数据
			curl_setopt($ch, CURLOPT_POSTFIELDS, $arr[1]);
		
		}else{
			curl_setopt($ch, CURLOPT_URL, $this->reqContent);
		}
		
		//设置证书信息
		if($this->certFile != "") {
			curl_setopt($ch, CURLOPT_SSLCERT, $this->certFile);
			curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $this->certPasswd);
			curl_setopt($ch, CURLOPT_SSLCERTTYPE, $this->certType);
		}
		
		//设置CA
		if($this->caFile != "") {
			// 对认证证书来源的检查，0表示阻止对证书的合法性的检查。1需要设置CURLOPT_CAINFO
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
			curl_setopt($ch, CURLOPT_CAINFO, $this->caFile);
		} else {
			// 对认证证书来源的检查，0表示阻止对证书的合法性的检查。1需要设置CURLOPT_CAINFO
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		}
		
		// 执行操作
		$res = curl_exec($ch);
		$this->responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
		
		if ($res == NULL) { 
		   $this->errInfo = "call http err :" . curl_errno($ch) . " - " . curl_error($ch) ;
		   curl_close($ch);
		   return false;
		} else if($this->responseCode  != "200") {
			$this->errInfo = "call http err httpcode=" . $this->responseCode  ;
			curl_close($ch);
			return false;
		}
		
		curl_close($ch);
		$this->resContent = $res;

		
		return true;
	}
	
	function getResponseCode() {
		return $this->responseCode;
	}
	
}

class ClientResponseHandler  {
	var $key;
	var $parameters;
	var $debugInfo;
	var $content;
	function __construct() {$this->ClientResponseHandler();}
	function ClientResponseHandler() {
		$this->key = "";
		$this->parameters = array();
		$this->debugInfo = "";
		$this->content = "";
	}
	function getKey() {return $this->key;}
	function setKey($key) {$this->key = $key;}
	function setContent($content) {
		$this->content = $content;
		$xml = simplexml_load_string($this->content);
		$encode = $this->getXmlEncode($this->content);
		if($xml && $xml->children()) {
			foreach ($xml->children() as $node){
				//有子节点
				if($node->children()) {
					$k = $node->getName();
					$nodeXml = $node->asXML();
					$v = substr($nodeXml, strlen($k)+2, strlen($nodeXml)-2*strlen($k)-5);
					
				} else {
					$k = $node->getName();
					$v = (string)$node;
				}
				
				if($encode!="" && $encode != "UTF-8") {
					$k = iconv("UTF-8", $encode, $k);
					$v = iconv("UTF-8", $encode, $v);
				}
				
				$this->setParameter($k, $v);			
			}
		}	
	}
	function setContent_backup($content) {
		$this->content = $content;
		$encode = $this->getXmlEncode($this->content);
		$xml = new SofeeXmlParser(); 
		$xml->parseFile($this->content); 
		$tree = $xml->getTree(); 
		unset($xml); 
		foreach ($tree['root'] as $key => $value) {
			if($encode!="" && $encode != "UTF-8") {
				$k = mb_convert_encoding($key, $encode, "UTF-8");
				$v = mb_convert_encoding($value[value], $encode, "UTF-8");								
			}
			else 
			{
				$k = $key;
				$v = $value[value];
			}
			$this->setParameter($k, $v);
		}
	}
	function getContent() {
		return $this->content;
	}
	function getParameter($parameter) {
		return $this->parameters[$parameter];
	}
	function setParameter($parameter, $parameterValue) {
		$this->parameters[$parameter] = $parameterValue;
	}
	function getAllParameters() {
		return $this->parameters;
	}	
	function isTenpaySign() {
		$signPars = "";
		ksort($this->parameters);
		foreach($this->parameters as $k => $v) {
			if("sign" != $k && "" != $v) {
				$signPars .= $k . "=" . $v . "&";
			}
		}
		$signPars .= "key=" . $this->getKey();
		$sign = strtolower(md5($signPars));
		$tenpaySign = strtolower($this->getParameter("sign"));
		$this->_setDebugInfo($signPars . " => sign:" . $sign .
				" tenpaySign:" . $this->getParameter("sign"));
		return $sign == $tenpaySign;
	}
	function getDebugInfo() {
		return $this->debugInfo;
	}
	function getXmlEncode($xml) {
		$ret = preg_match ("/<?xml[^>]* encoding=\"(.*)\"[^>]* ?>/i", $xml, $arr);
		if($ret) {
			return strtoupper ( $arr[1] );
		} else {
			return "";
		}
	}
	function _setDebugInfo($debugInfo) {
		$this->debugInfo = $debugInfo;
	}
	function _isTenpaySign($signParameterArray) {
		$signPars = "";
		foreach($signParameterArray as $k) {
			$v = $this->getParameter($k);
			if("sign" != $k && "" != $v) {
				$signPars .= $k . "=" . $v . "&";
			}			
		}
		$signPars .= "key=" . $this->getKey();
		$sign = strtolower(md5($signPars));
		$tenpaySign = strtolower($this->getParameter("sign"));
		$this->_setDebugInfo($signPars . " => sign:" . $sign .
				" tenpaySign:" . $this->getParameter("sign"));
		return $sign == $tenpaySign;		
	}
}
?>