341 lines
11 KiB
PHP
341 lines
11 KiB
PHP
<?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;
|
||
}
|
||
}
|
||
/******************************* 小程序公用 ******************************************/
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
?>
|