<?php
namespace app\app\controller;
use think\Controller;
use think\Db;
/**
 * 验证api是否正确
 */
class Base extends controller
{

	function __initialize()
	{

	}
	/******************************* 小程序公用 ******************************************/
	/**
	 * 检验数据的真实性，并且获取解密后的明文.
	 * @param $encryptedData string 加密的用户数据
	 * @param $iv string 与用户数据一同返回的初始向量
	 * @param $data string 解密后的原文
	 * @return int 成功0，失败返回对应的错误码
	 */
	public function decryptData($sessionKey,$appid,$encryptedData, $iv, &$data )
	{
		$OK = 0;
		$IllegalAesKey = -41001;
		$IllegalIv = -41002;
		$IllegalBuffer = -41003;
		$DecodeBase64Error = -41004;
		if (strlen($sessionKey) != 24) {
			return $IllegalAesKey;
		}
		$aesKey=base64_decode($sessionKey);


		if (strlen($iv) != 24) {
			return $IllegalIv;
		}
		$aesIV=base64_decode($iv);

		$aesCipher=base64_decode($encryptedData);
		$result=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
		$dataObj=json_decode( $result );
		if( $dataObj  == NULL )
		{
			return $IllegalBuffer;
		}
		if( $dataObj->watermark->appid != $appid )
		{
			return $IllegalBuffer;
		}
		$data = $result;
		return $OK;
	}
	/**
	*GET方式请求
	*/
	 function httpGet($url){
	    	$ch = curl_init();
		//设置选项，包括URL
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HEADER, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不验证证书
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //不验证证书
		//执行并获取HTML文档内容
		$output = curl_exec($ch);
		//释放curl句柄
		curl_close($ch);
		//$result = json_decode($output);
		//$result->subscribe
		return $output;
	}
	/**
	 * post访问url
	 */
	function https_post($url,$data){
		$ch = curl_init();
		curl_setopt($ch,CURLOPT_URL,$url);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不验证证书
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //不验证证书
		curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
		curl_setopt($ch,CURLOPT_POST,1);
		curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
		$output = curl_exec($ch);
		curl_close($ch);
		return $output;
	}
	/*
	*加解密
  	*函数authcode($string, $operation, $key, $expiry)中的$string：字符串，明文或密文；$operation：DECODE表示解密，其它表示加密；$key：密匙；$expiry：密文有效期。
  	*/
	 public function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
		    // 动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙
		    $ckey_length = 4;

		    // 密匙
		    $key = md5($key ? $key : '' ); //md5($key ? $key : $GLOBALS['discuz_auth_key']);

		    // 密匙a会参与加解密
		    $keya = md5(substr($key, 0, 16));
		    // 密匙b会用来做数据完整性验证
		    $keyb = md5(substr($key, 16, 16));
		    // 密匙c用于变化生成的密文
		    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
		    // 参与运算的密匙
		    $cryptkey = $keya.md5($keya.$keyc);
		    $key_length = strlen($cryptkey);
		    // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，
		    //解密时会通过这个密匙验证数据完整性
		    // 如果是解码的话，会从第$ckey_length位开始，因为密文前$ckey_length位保存 动态密匙，以保证解密正确
		    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) :  sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
		    $string_length = strlen($string);
		    $result = '';
		    $box = range(0, 255);
		    $rndkey = array();
		    // 产生密匙簿
		    for($i = 0; $i <= 255; $i++) {
		        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
		    }
		    // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度
		    for($j = $i = 0; $i < 256; $i++) {
		        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
		        $tmp = $box[$i];
		        $box[$i] = $box[$j];
		        $box[$j] = $tmp;
		    }
		    // 核心加解密部分
		    for($a = $j = $i = 0; $i < $string_length; $i++) {
		        $a = ($a + 1) % 256;
		        $j = ($j + $box[$a]) % 256;
		        $tmp = $box[$a];
		        $box[$a] = $box[$j];
		        $box[$j] = $tmp;
		        // 从密匙簿得出密匙进行异或，再转成字符
		        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
		    }
		    if($operation == 'DECODE') {
		        // 验证数据有效性，请看未加密明文的格式
		        if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) &&  substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
		            return substr($result, 26);
		        } else {
		            return '';
		        }
		    } else {
		        // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因
		        // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码
		        return $keyc.str_replace('=', '', base64_encode($result));
		    }
	}
/**
*微信提现，企业付款到零用钱
*$openid 公众号下的用户openid
*$mch_billno 订单号
*$total_amount 提现的金额
*$mark 备注
*@return 错误 false ，成功
*/
function wxBusinessPay($openid,$mch_billno,$total_amount,$mark,$MCHID,$APPID,$Key){
		$nonce_str=$this->createNoncestr( $length = 32 );
		$sendarry=array();
		// 随机字符串
		$sendarry["nonce_str"]=$nonce_str;
		// 商户订单号
		$sendarry["partner_trade_no"]=$mch_billno;
		// 商户号
		$sendarry["mchid"]=$MCHID;
		// 公众账号appid
		$sendarry["mch_appid"]=$APPID;
		// 用户openid
		$sendarry["openid"]=$openid;
		// 付款金额
		$sendarry["amount"]=$total_amount*100;
		// 红包祝福语
		$sendarry["check_name"]='NO_CHECK';//不超过20字
		// Ip地址
		$sendarry["spbill_create_ip"]='62.234.183.107';
		// 备注
		$sendarry["desc"]=$mark;
		// 签名
		$sign=$this->getSign($sendarry,$Key);
		$sendarry["sign"]=$sign;
		//转换XML
		$sendxml=$this->arrayToXml($sendarry);
		// 调用的接口
		$url='https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
		// 使用证书，以post方式提交xml到对应的接口url
		$data=$this->postXmlSSLCurl($sendxml,$url,$seconed=30);
		if($data == false){
			return false;
		}else{
			$obj = $this->xmlToArray($data);
			//发送成功
			if($obj['return_code'] == 'SUCCESS' && $obj['result_code'] == 'SUCCESS'){
				return true;
			//金额不足
			}else{
				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;
	}
	/**
	 * 	作用：格式化参数，签名过程需要使用
	 */
	function formatBizQueryParaMap($paraMap, $urlencode)
	{
		$buff = "";
		ksort($paraMap);
		foreach ($paraMap as $k => $v)
		{
		    if($urlencode)
		    {
			   $v = urlencode($v);
			}
			//$buff .= strtolower($k) . "=" . $v . "&";
			$buff .= $k . "=" . $v . "&";
		}
		$reqPar;
		if (strlen($buff) > 0)
		{
			$reqPar = substr($buff, 0, strlen($buff)-1);
		}
		return $reqPar;
	}
	/**
	 * 	作用：生成签名
	 */
	public function getSign($Obj,$KEY)
	{
		foreach ($Obj as $k => $v)
		{
			$Parameters[$k] = $v;
		}
		//签名步骤一：按字典序排序参数
		ksort($Parameters);
		$String = $this->formatBizQueryParaMap($Parameters, false);
		//echo '【string1】'.$String.'</br>';
		//签名步骤二：在string后加入KEY
		$String = $String."&key=".$KEY;
		//echo "【string2】".$String."</br>";
		//签名步骤三：MD5加密
		$String = md5($String);
		//echo "【string3】 ".$String."</br>";
		//签名步骤四：所有字符转为大写
		$result_ = strtoupper($String);
		//echo "【result】 ".$result_."</br>";
		return $result_;
	}
		/**
	 * 	作用：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;
    }

	/**
	 * 	作用：将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到对应的接口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, ROOT_PATH."/application/app/cacert/apiclient_cert.pem");
		//默认格式为PEM，可以注释
		curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
		curl_setopt($ch,CURLOPT_SSLKEY, ROOT_PATH."/application/app/cacert/apiclient_key.pem");
		//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;
		}
	}
	/******************************* 小程序公用 ******************************************/
}





?>
