<?php
// 버전 1.0 : 2014/11/06  문석호, 최초 작성
/**
 * 2014.12.02 : 1) 로깅 시 주요 정보 마스킹 처리, 2) PayMethod key check
 */
    class CnsPayWebConnector {
        private $LogPath = "";
        private $ActionUrl = "";
    	private $cancelUrl = "";
    	private $phpVersion = "";
    	private $encodeKey = "";
    	private $requestData = array();
    	private $resultData = array();
    	public function CnsActionUrl($url) {
    		$this->ActionUrl = $url;
    	}
    	public function CnsPayVersion($ver) {
    		$this->phpVersion = $ver;
    	}
    	public function CnsPayWebConnector($LogDir) {
    	    $this->cancelUrl = $this->ActionUrl."/lite/cancelProcess.jsp";
    	    if (substr($LogDir, strlen($LogDir) - 1) == "/") {
    	        $LogDir = substr($LogDir, 0, strlen($LogDir) - 1);
    	    }
    	    @mkdir($LogDir);
    	    $this->LogPath = $LogDir."/";
    	}
    	public function setRequestData($request) {
    	    try {
        		foreach (array_keys($request) as $key) {
                    if(is_array($request[$key]))
                        continue;

                    $this->requestData[$key] = iconv("UTF-8", "EUC-KR", $request[$key]);
                }
                return "_TRUE_";
            } catch (Exception $ex) {
        	    $this->writeLog("setRequestData() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
        }
        public function addRequestData($key, $value) {
    	    try {
    	        $this->requestData[$key] = $value;
    	        return "_TRUE_";
    	    } catch (Exception $ex) {
        	    $this->writeLog("addRequestData() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}
        public function getResultData($key) {
            try {
        		if (!in_array($key, array_keys($this->resultData))) {
        			return "";
        		} else if ($key == "Amt") {
        			if ($this->resultData[$key] != null && $this->resultData[$key] != "null" && $this->resultData[$key] != "") {
        				return $this->resultData[$key];
        			} else {
        			    return "0";
        			}
        		}
        		return $this->resultData[$key];
        	} catch (Exception $ex) {
        	    $this->writeLog("getResultData() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}

    	// 2014.12.02 추가 (check key in array)
    	private function getRequestData($key) {
    		if (array_key_exists($key, $this->requestData)) {
    			return $this->requestData[$key];
    		} else {
    			return "";
    		}
    	}

    	public function requestAction() {
    		$encodeKey = $this->requestData["EncodeKey"];
    		unset($this->requestData["EncodeKey"]);
    	    try {
        		if ($this->requestData["actionType"] != "CL0" && $this->requestData["actionType"] != "CI0") {
        		    if ($this->getRequestData("PayMethod") != "ESCROW") {
        		        $this->requestData["TID"] = $this->generateTID($this->requestData["MID"], $this->getRequestData("PayMethod"));
        		    }
        		}
        		$serviceUrl = $this->setActionType($this->requestData["actionType"], $this->getRequestData("PayMethod"));
                if ($serviceUrl == "_FAIL_" || $serviceUrl == "CNSPAY_10") {
                    $this->resultData["ResultCode"] = "JL10";
        			$this->resultData["ResultMsg"] = "actionType 설정이 잘못되었습니다.";
                    return "_FAIL_";
                }
                $this->writeLog("Request");
                $this->writeLog($this->requestData);
                $requestMessage = $this->makeRequestText($this->requestData);
                $resultMessage = $this->connectToServer($serviceUrl, $requestMessage);
                $this->writeLog("Result");
                // 2014.12.02 수신 전문 로깅 처리 제외
                //$this->writeLog($resultMessage);
    			if ($resultMessage == "_FAIL_" || substr($resultMessage, 0, 4) == "FAIL") {
    			    $resultCode = "";
    			    $resultMsg = "";
    			    $netCancelFlag = $this->requestNetCancel();
                    if ($netCancelFlag == "_TRUE_") {
    					$resultCode = "JL32";
        				$resultMsg = "PGWEB서버 통신중 오류가 발생하였습니다. (NET_CANCEL)";
    				} else { // netCancel 실패이면,
        				$resultCode = "JL33";
    					$resultMsg = "네트웍이 불안정으로 승인 실패하였습니다. 결제가 비 정상 처리 될 수 있으니 거래내역을 반드시 확인해주십시오.";
        			}
    				$this->resultData["ResultCode"] = $resultCode;
        			$this->resultData["ResultMsg"] = $resultMsg;
    				return "_FAIL_";
    			}
    			$resultMessage = $this->parseResult($resultMessage);
    			//$this->writeLog($this->resultData);
    			// 2014.12.02 로깅 시 주요 데이터 마스킹 처리
    			$this->writeLog($this->resultDataMask($this->resultData));
    			if ($resultMessage == "_FAIL_" || $resultMessage == "CNSPAY_41") {
        		    $this->resultData["ResultCode"] = "JL41";
        			$this->resultData["ResultMsg"] = "응답전문이 없습니다.";
                    return "_FAIL_";
    			}
    			return "_TRUE_";
    		} catch (Exception $ex) {
        	    $this->writeLog("requestAction() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}

    	// 2014.12.02 결과 배열 마스킹
    	private function resultDataMask($strLogText) {
    		$arrMask = array();
    		if (is_array($strLogText)) {
	    		foreach (array_keys($strLogText) as $key) {
	    			$k = str_replace("\n", "", trim($key));
	    			$arrMask[$k] = $this->requestMask($k, $strLogText[$key]);
	    		}
	    		return $arrMask;
    		} else {
    			return str_replace("\n", "", trim($strLogText));
    		}
    	}

    	// 2014.12.02 주요 정보 마스킹
    	private function requestMask ($name, $text) {
    		$value = str_replace("\n", "", trim($text));

    		if ($value == null || strlen(trim($value)) == 0) return "";

			if ($name == "X_CARDNO" || $name == "realPan" || $name == "cardNo"
					|| $name == "CardBin" || $name == "CardNo") {
				return $this->masking($value, 6, true, false);
			} else if ($name == "BuyerName" || $name == "buyerName") {
				return $this->masking($value, 1, true, false);
			} else if ($name == "BuyerEmail") {
				return $this->masking($value, 6, false, true);
			} else if ($name == "BuyerTel" || $name == "DstAddr") {
				return $this->masking($value, 5, false, false);
			} else if ($name == "BuyerAddr") {
				return $this->masking($value, 6, true, false);
			} else if ($name == "UserIP" || $name == "MallIP" || $name == "CancelPwd"
					|| $name == "mallUserID" || $name == "MallUserID"
					|| $name == "CancelIP") {
				return $this->masking($value, mb_strlen(iconv('euc-kr','utf-8',$value), 'utf-8'), true, true);
			} else {
				return $value;
			}
		}

		// 2014.12.02 마스킹 처리
		private function masking($string, $num, $isLeftOrder, $beginMasking) {

			if ( $string == null )
				return "";

			$res = "";
			$res2 = "";
			$sleng = 0;

			$str = iconv('euc-kr','utf-8',$string);
			$n = mb_strlen($str, 'utf-8');

			if ( $num >= 1 ) {
				if ( $n < $num ) {
					$res = $str;
				} else {
					if($beginMasking) {
						if ($isLeftOrder) {
							$res = str_repeat("*", $n);
						} else {
							$sleng = $num;
							$res2 = mb_substr($str, $sleng, $n, 'utf-8');
							for ( $j = 0; $j < $sleng; $j++ ) {
								$res .= "*";
							}
							$res .= $res2;
						}
					} else {
						$sleng = $num;
						$res2 = mb_substr($str, 0, $sleng, 'utf-8');
						for ( $j = $sleng; $j < $n; $j++ ) {
							$res .= "*";
						}
						$res = $res2 . $res;
					}
				}
			} else {
				$res = $str;
			}

			return iconv('utf-8','euc-kr',$res);
		}

    	private function requestNetCancel() {
    	    try {
        		// 예기치 못한 오류인경우 망상취소 시도.
        		$serviceUrl = $this->cancelUrl;
        		$this->requestData["actionType"] = "CL0";
        		$this->requestData["CancelIP"] = $this->requestData["MallIP"];
        		if ($this->requestData["Amt"] == null) {
        		    return "_FAIL_";
        		} else {
        		    if (is_numeric($this->requestData["Amt"])) {
        			    $this->requestData["CancelAmt"] = $this->requestData["Amt"];
        			} else {
        			    $this->requestData["CancelAmt"] = parameterDecrypt($encodeKey, $this->requestData["Amt"]);
        			}
        		}
        		$this->requestData["CancelMsg"] = "NICE_NET_CANCEL";
        		$this->requestData["PartialCancelCode"] = "0";
        		$this->requestData["NetCancelCode"] = "1";
        		if ($this->getRequestData("PayMethod") == "BILL" || $this->getRequestData("PayMethod") == "KAKAOPAY") $this->requestData["PayMethod"] = "CARD";
        		$requestMessage = makeRequestText($this->requestData);
        	    $resultMessage = connectToServer($serviceUrl, $this->requestData);
        		if ($resultMessage == "_FAIL_" || substr($resultMessage, 0, 4) == "FAIL") {
    			    $resultMessage = connectToServer2($serviceUrl, $this->requestData, 20);
    			    if ($resultMessage == "_FAIL_" || substr($resultMessage, 0, 4) == "FAIL") {
        			    //$this->resultData["ResultCode"] = "JL41";
            			//$this->resultData["ResultMsg"] = "망상취소 오류";
            			return "_FAIL_";
    			    }
    			}
        		return "_TRUE_";
    		} catch (Exception $ex) {
        	    $this->writeLog("requestNetCancel() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}
    	private function generateTID($mid, $svcCd) {
    	    try {
        		$iRandom = str_pad(rand(0, 9999), 4, "0", STR_PAD_LEFT);
        		return $mid.$this->getSvcCd($svcCd)."01".date("ymdHis").$iRandom;
        	} catch (Exception $ex) {
        	    $this->writeLog("generateTID() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}
    	private function getSvcCd($svcCd) {
    	    try {
        		if ($svcCd == "CARD" || $svcCd == "BILL" || $svcCd == "KAKAOPAY") {
        			return "01";
        		} else if ($svcCd == "BANK") {
        			return "02";
        		} else if ($svcCd == "VBANK") {
        			return "03";
        		} else if ($svcCd == "CELLPHONE") {
        			return "05";
        		} else if ($svcCd == "MOBILE_BILLING") {
        			return "05";
        		} else if ($svcCd == "MOBILE_BILL") {
        			return "05";
        		}
        		return "00";
        	} catch (Exception $ex) {
        	    $this->writeLog("getSvcCd() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}
    	private function setActionType($type, $paymethod) {
    	    try {
        		if ($type == null) return "CNSPAY_10";
        		$builder = $this->ActionUrl;
        		if ($type == "CL0") {
        			$builder = $builder."/lite/cancelProcess.jsp";
        		} else if ($type == "CI0") {
        			$builder = $builder."/lite/tidInfoProcess.jsp";
        		} else if ($type == "PY0") {
        			if ($paymethod == "CASHRCPT") { // 현금영수증인경우
        				$builder = $builder."/lite/cashReceiptProcess.jsp";
        			} else if ($paymethod == "BILL") {
        				$builder = $builder."/lite/billingProcess.jsp";
        			} else if ($paymethod == "BILLKEY") {
        				$builder = $builder."/lite/billkeyProcess.jsp";
        			} else if ($paymethod == "ESCROW") {
        				$builder = $builder."/lite/escrowProcess.jsp";
        			} else if ($paymethod == "MOBILE_AUTH") {
        				$builder = $builder."/lite/mobileAuth.jsp";
        			} else if ($paymethod == "MOBILE_BILL") {
        				$builder = $builder."/lite/mobileBill.jsp";
        			} else if ($paymethod == "MOBILE_BILLING") {
        				$builder = $builder."/lite/mobileBillingProcess.jsp";
        			} else if ($paymethod == "MOBILE_AUTH_REQ") {
        				$builder = $builder."/lite/mobileConfirmRequest.jsp";
        			} else if ($paymethod == "MOBILE_AUTH_RES") {
        				$builder = $builder."/lite/mobileConfirmResult.jsp";
        			} else if ($paymethod == "CARD_ARS") {
        				$builder = $builder."/lite/cardArsProcess.jsp";
        			} else if ($paymethod == "MOBILE_AUTH_NS") {
        				$builder = $builder."/lite/mobileAuth_NS.jsp";
        			} else if ($paymethod == "OM_SUB_INS") {
        				$builder = $builder."/lite/payproxy/subMallSetProcess.jsp";
        			} else if ($paymethod == "OM_SUB_PAY") {
        				$builder = $builder."/lite/payproxy/subMallIcheProcess.jsp";
        			} else if ($paymethod == "LOTTE_POINT") {
        				$builder = $builder."/api/checkLottePoint.jsp";
        			} else if ($paymethod == "HPBILLKEY") {
        				$builder = $builder."/lite/hpBillkeyProcess.jsp";
        			} else if ($paymethod == "HPCARD_AUTH") {
        				$builder = $builder."/lite/hpCardAuthProcess.jsp";
        			} else if ($paymethod == "HPCARD_BILLKEY") {
        				$builder = $builder."/lite/hpCardBillkeyProcess.jsp";
        			} else {
        				$builder = $builder."/lite/payProcess.jsp";
        			}
        		}
        		return $builder;
        	} catch (Exception $ex) {
        	    $this->writeLog("setActionType() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}
    	private function makeRequestText($reqData) {
    	    try {
        		$strParameter = "";
                foreach (array_keys($reqData) as $key) {
                    $strParameter = $strParameter.$key."=".urlencode($reqData[$key])."&";
                }
                $strParameter = substr($strParameter, 0, strlen($strParameter) - 1);
                return $strParameter;
            } catch (Exception $ex) {
        	    $this->writeLog("makeRequestText() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}
    	private function connectToServer($urlStr, $reqData) {
    	    try {
    		    return $this->connectToServer2($urlStr, $reqData, 15);
    		} catch (Exception $ex) {
        	    $this->writeLog("connectToServer() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}
    	private function connectToServer2($urlStr, $reqData, $timeout) {
    	    try {
        	    // php에 cURL 모듈 설치 필요(리눅스 - curl.so, 윈도우 - php_curl.dll 확장모듈 필요)
                $ch = curl_init();
                curl_setopt($ch, CURLOPT_URL, $urlStr); //접속할 URL 주소
                //curl_setopt($ch, CURLOPT_PORT, 6464); //접속할 port, 주소에 있으므로 설정하지 않음
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // 인증서 체크같은데 true 시 안되는 경우가 많다.
                //curl_setopt($ch, CURLOPT_SSLVERSION, 3); // SSL 버젼 (https 접속시에 필요, 기본값으로 해야하므로 설정하지 않음)
                curl_setopt($ch, CURLOPT_HEADER, 0); // 헤더 출력 여부
                curl_setopt($ch, CURLOPT_POST, 1); // Post Get 접속 여부
                curl_setopt($ch, CURLOPT_POSTFIELDS, $reqData); // Post 값 Get 방식처럼적는다.
                curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); // TimeOut 값
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 결과값을 받을것인지
                curl_setopt($ch, CURLOPT_USERAGENT, $this->phpVersion); // 버전
                $result = curl_exec($ch);
                $errcode = curl_error($ch);
                if ($errcode != "") $result = $errcode;
                //$errcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                //if ($errcode != 200) $result = $errcode;
                curl_close($ch);
                return $result;
            } catch (Exception $ex) {
                $this->writeLog("connectToServer2() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
                return "_FAIL_";
            }
    	}
    	private function parseResult($resultMessage) {
    	    try {
        		if ($resultMessage == null) return "CNSPAY_41";
        		$parsedArr = explode("|", $resultMessage);
        		foreach ($parsedArr as $valueArr) {
        		    $posit = strpos($valueArr, "=");
                    $key = substr($valueArr, 0, $posit);
                    $value = substr($valueArr, $posit + 1);
                    $this->resultData[$key] = $value;
                }
                return "_TRUE_";
            } catch (Exception $ex) {
                $this->writeLog("parseResult() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
                return "_FAIL_";
            }
    	}
    	public function writeLog($strLogText) {
    	    $log_string = "";
            $exclude = array('MID', 'merchantEncKey', 'merchantHashKey', 'CancelPwd', 'site_cd', 'def_site_cd', 'CST_MID', 'LGD_MID');
    	    if (is_array($strLogText)) {
    	        $log_string = "[".date("Y/m/d H:i:s")."] \r\n";
    	        foreach (array_keys($strLogText) as $key) {
                    if(in_array($key, $exclude))
                        continue;

                    if(preg_match('#^od_.+$#', $key))
                        continue;

                    $log_string = $log_string."                      [".$key."] => ".$strLogText[$key]."\r\n";
                }
            } else {
                $log_string = "[".date("Y/m/d H:i:s")."] ".$strLogText."\r\n";
            }
            $log_filenm = $this->LogPath.date("Ymd")."_CNSpay.log";
            $log_file = fopen($log_filenm, "a");
            if($log_file == false) return;
            flock($log_file, LOCK_EX);
            //fwrite($log_file, $log_string);
            fputs($log_file, $log_string);
            fflush($log_file);
            flock($log_file, LOCK_UN);
            fclose($log_file);
        }
        public function makeDateString($sDate) {
            try {
        		if ($sDate == null) return "";
        		$strValue = "";
        		if (strlen($sDate) == 12) {
        			$strValue = $strValue."20".substr($sDate, 0, 2)."-";
        			$strValue = $strValue.substr($sDate, 2, 2)."-";
        			$strValue = $strValue.substr($sDate, 4, 2). " ";
        			$strValue = $strValue.substr($sDate, 6, 2).":";
        			$strValue = $strValue.substr($sDate, 8, 2).":";
        			$strValue = $strValue.substr($sDate, 10, 2);
        		} else if (strlen($sDate) == 14) {
        			$strValue = $strValue.substr($sDate, 0, 4)."-";
        			$strValue = $strValue.substr($sDate, 4, 2)."-";
        			$strValue = $strValue.substr($sDate, 6, 2)." ";
        			$strValue = $strValue.substr($sDate, 8, 2).":";
        			$strValue = $strValue.substr($sDate, 10, 2).":";
        			$strValue = $strValue.substr($sDate, 12, 2);
        		} else if (strlen($sDate) == 8) {
        		    $strValue = $strValue.substr($sDate, 0, 4)."-";
        			$strValue = $strValue.substr($sDate, 4, 2)."-";
        			$strValue = $strValue.substr($sDate, 6, 2);
        		} else {
        			$strValue = $sDate;
        		}
        		return $strValue;
        	} catch (Exception $ex) {
        	    writeLog("makeDateString() Exception Code ".$ex->getCode()." : ".$ex->getMessage()." in ".$ex->getFile()." on line ".$ex->getLine());
        	    return "_FAIL_";
        	}
    	}


      public function makeHashInputString($salt) {

        $result = "";

				for($count = 0;$count < strlen($salt)/2;$count++) {
					$temp0 = substr($salt, 2*$count, 2);
					$temp1 = hexdec($temp0);
					$temp3 = reset(unpack("l", pack("l", $temp1 +0xffffff00)));
					$temp4 = pack('C*', $temp3);

					$result = $result.$temp4;

				}

				return $result;

    	}

    }
?>
