<?php

use think\facade\Config;
use think\facade\Db;
use think\facade\Log;
use think\facade\Cache;
use app\Constant;
// 应用公共文件
/**
* m_开头的是 业务通用方法
* c 开头是基础方法
* 有些不是m和c开头的.是基础方法,
*/

/**
* 快捷方法
*
* @param [type] $name
*/
function D($name) {
  return Db::table($name, [], false);
}
function DQ($sql, $value = []) {
  return Db::query($sql, $value);
}
function D2($name) {
  return Db::connect('mysql2')->table($name, [], false);
}
function DQ2($sql, $value = []) {
  return Db::connect('mysql2')->query($sql, $value);
}
/**
* 加密
*
* @param string $string
* @param string $skey
*/
function a_encode($string = "", $skey = '') {
  if (strlen($skey) === 0) {
    $skey = Config::get('app.ENCODE_KEY');
  }
  $data = openssl_encrypt($string, 'AES-128-ECB', $skey, OPENSSL_RAW_DATA);
  $data = strtolower(bin2hex($data));
  return $data;
}

/**
* 解密
*
* @param string $string
* @param string $skey
*/
function a_decode($string = "", $skey = '') {
  if (strlen($skey) === 0) {
    $skey = Config::get('app.ENCODE_KEY');
  }
  if (empty($string) || strlen($string) % 2 == 1) {
    return '';
  }
  return openssl_decrypt(hex2bin($string), 'AES-128-ECB', $skey, OPENSSL_RAW_DATA);
}


/**
* https get
*
* @param [string] $url
* @return string
*/
function curl_get_https($url) {
  $curl = curl_init(); // 启动一个CURL会话
  curl_setopt($curl, CURLOPT_URL, $url);
  curl_setopt($curl, CURLOPT_HEADER, 0);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
  curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); // 从证书中检查SSL加密算法是否存在
  $tmpInfo = curl_exec($curl); //返回api的json对象
  //关闭URL请求
  curl_close($curl);
  return $tmpInfo; //返回json对象
}

function http_post_json($url, $jsonStr, $header = []) {
  c_debug('http_post_json:'.$jsonStr . '----$header:' . json_encode($header));
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt(
    $ch,
    CURLOPT_HTTPHEADER,
    array_merge(
      $header,
      [
        'Content-Type: application/json; charset=utf-8',
        'Content-Length: ' . strlen($jsonStr),
      ]
    )
  );
  $response = curl_exec($ch);
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  return $response;
}

function c_post_http($url, $data) {
  $data = http_build_query($data);
  $opts = array('http' => array('method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $data));
  $context = stream_context_create($opts);
  return file_get_contents($url, false, $context);
}
/**
* https post
*
* @param [string] $url
* @param [array(k=>v)] $data
* @return string
*/
function curl_post_https($url, $data, $opt = []) {
  $opt = array_merge([
    'path_cert' => '',
    'path_key' => '',
    'header' => []
  ], $opt);
  // Log::write(json_encode($opt) . '== curl_post_https', 'notice');
  $with_ca = false;
  if ($opt && $opt['path_cert'] && $opt['path_key']) {
    $with_ca = true;
  }
  // 模拟提交数据函数
  $curl = curl_init(); // 启动一个CURL会话
  curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $with_ca); // 对认证证书来源的检查
  if ($with_ca) {
    curl_setopt($curl, CURLOPT_SSLCERT, $opt['path_cert']);
    curl_setopt($curl, CURLOPT_SSLKEY, $opt['path_key']);
  }
  curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); // 从证书中检查SSL加密算法是否存在
  curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
  curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
  curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
  curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
  curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
  curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
  curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
  curl_setopt($curl, CURLOPT_HTTPHEADER, $opt['header']);
  $tmpInfo = curl_exec($curl); // 执行操作
  if (curl_errno($curl)) {
    echo 'Errno' . curl_error($curl); //捕抓异常
  }
  curl_close($curl); // 关闭CURL会话
  return $tmpInfo; // 返回数据，json格式
}
function ajaxSuccessPage($data = "", $current = 1, $pageSize = 10, $total = 0, $code = 0, $only_json = false) {
    $json = array(
        "success" => true,
        "errorMsg" => "",
        "errorCode" => $code,
        "model" => $data,
        "current" => $current,
        "pageSize" => $pageSize,
        "total" => $total,
    );
    return $only_json ? $json : json($json);
}

function ajaxSuccess($data = "", $code = '0', $only_json = false) {
  $json = array(
    "success" => true,
    "errorMsg" => "",
    "errorCode" => $code,
    "model" => $data,
  );
  return $only_json ? $json : json($json);
}

function ajaxFail($errorMsg = "", $errorCode = -1, $data = null, $only_json = false) {
  $json = array(
    "success" => false,
    "errorMsg" => $errorMsg,
    "errorCode" => $errorCode,
    "model" => $data,
  );
  return $only_json ? $json : json($json);
}

/**
* 解密微信加密数据
*/
function decrypt_wx_data($encrypted_data, $iv, $session_key) {
  if (strlen($session_key) != 24) {
    return -1;
  }
  if (strlen($iv) != 24) {
    return -2;
  }
  $aes_cipher = base64_decode($encrypted_data);
  $aes_key = base64_decode($session_key);
  $aes_iv = base64_decode($iv);
  $result = openssl_decrypt($aes_cipher, "AES-128-CBC", $aes_key, 1, $aes_iv);
  $data = json_decode($result, true);
  if ($data == null) {
    Log::write([
      'encrypted_data' => $encrypted_data,
      'iv' => $iv,
      'session_key' => $session_key,
      'result' => $result,
    ], 'notice');
    return -3;
  }
  return $data;
}

/**
* 获取session_key
*/
function code_2_session($js_code) {
  $appid = Config::get('app.APPID');
  $secret = Config::get('app.SECERT_KEY');
  $url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . $appid . '&secret=' . $secret . '&js_code=' . $js_code . '&grant_type=authorization_code';
  $result = curl_get_https($url);
  think\facade\Log::record($url . '--' . $result, 'notice');
  $result_json = json_decode($result, true);
  if (array_key_exists('errcode', $result_json) && $result_json['errcode'] != 0) {
    think\facade\Log::record($result, 'notice');
    return null;
  }
  return $result_json;
}
/**
*随机字符串数字
*
* @param integer $len
*/
function random_vcode($len = 6) {
  srand((float) microtime() * 1000000); //create a random number feed.
  $ychar = "0,1,2,3,4,5,6,7,8,9";
  $list = explode(",", $ychar);
  $authnum = '';
  for ($i = 0; $i < $len; $i++) {
    $randnum = rand(0, 9); // 10+26;
    $authnum .= $list[$randnum];
  }
  return $authnum;
}
function rad($d) {
  return $d * M_PI / 180.0;
}

function random_password($len = 6) {
  srand((float) microtime() * 1000000); //create a random number feed.
  $ychar = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z";
  $list = explode(",", $ychar);
  $authnum = '';
  for ($i = 0; $i < $len; $i++) {
    $randnum = rand(0, count($list)-1); // 10+26;
    $authnum .= $list[$randnum];
  }
  return $authnum;
}

/**
* 计算两个经纬度距离
*/
function c_lon_lat_distance($lon1, $lat1, $lon2, $lat2) {
  $radLat1 = rad($lat1);
  $radLat2 = rad($lat2);
  $a = $radLat1 - $radLat2;
  $b = rad($lon1) - rad($lon2);
  $s = 2 * asin(
    sqrt(
      pow(sin($a / 2), 2)
      + cos($radLat1)
      * cos($radLat2)
      * pow(sin($b / 2), 2)
    )
  );
  return ceil($s * 6378137.0);
}

/**
* 分隔成数组
*
* @param [type] $str
* @return array
*/
function c_dot_str_2_array($str): array
{
  $list = [];
  if ($str) {
    $list = explode(',', $str);
  }
  return $list;
}

/**
* 对象筛选属性
*
* @param array $obj
* @param array $keys
* @param boolean $pass_keys 是否去掉传入的属性
*/
function c_filter_property($obj, $keys, $pass_keys = false) {
  $re = [];
  if (!$pass_keys) {
    foreach ($keys as $k) {
      if (array_key_exists($k, $obj)) {
        $re[$k] = $obj[$k];
      }
    }
  } else {
    // echo in_array('c',['a','b']) ? 'in--':'on==';
    foreach ($obj as $k => $v) {
      if (!in_array($k, $keys)) {
        $re[$k] = $v;
      }
    }
  }
  return $re;
}

/**
* 列表筛选属性
*
* @param [type] $list
* @param [type] $keys
* @param boolean $pass_keys 是否去掉传入的属性
*/
function c_filter_property_list($list, $keys, $pass_keys = false) {
  $re_list = [];
  foreach ($list as $one) {
    $re_list[] = c_filter_property($one, $keys, $pass_keys);
  }
  return $re_list;
}

/**
* 生成订单号
*
* @param [type] $order_type
* @return string
*/
function s_order_id_render($order_type) {
  //1+1+2+2+2+2+2+2=14
  $y = substr(date('y'), 1, 1);
  $pre = $y . date('mdHis');
  $s_map = Cache::get(Constant::$ORDER_S_PTR);

  if (isset($s_map['pre']) && $s_map['pre'] == $pre) {
    $s_map['ptr'] += 1;
  } else {
    $s_map = ['pre' => $pre,
      'ptr' => 1];
  }

  Cache::set(Constant::$ORDER_S_PTR, $s_map);

  $ptr = $s_map['ptr'];
  $ptr = sprintf('%02d', $ptr);

  return $order_type . $pre . $ptr;
}
/**
* 生成订单核销码
*
* @param [type] $order_id
* @return void
*/
function s_order_use_code($order_id) {
  return substr($order_id, -6);
}

function c_read_xml_to_map($res) {
  /*<xml>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <return_msg><![CDATA[OK]]></return_msg>
  <appid><![CDATA[wx3c5f8f6a8ff6954b]]></appid>
  <mch_id><![CDATA[1579360191]]></mch_id>
  <nonce_str><![CDATA[cp6wwNef4GOeHyk2]]></nonce_str>
  <sign><![CDATA[91F7CC6640922C8ABA5960778ADE30B1]]></sign>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <prepay_id><![CDATA[wx2401465457053258a360ba621918835100]]></prepay_id>
  <trade_type><![CDATA[JSAPI]]></trade_type>
  </xml>
  */
  // $res = str_replace(' ', '', $res);
  // $res = str_replace('<xml>', '', $res);
  // $res = str_replace('</xml>', '', $res);
  // $res = str_replace("\r", "\n", $res);
  // $res = str_replace("\n\n", "\n", $res);
  // $res = trim($res);
  // $res = str_replace("\n", '&', $res);
  // $res = str_replace(']', '', $res);
  // $res = str_replace('<![CDATA[', '', $res);
  // $res = str_replace('>', '=', $res);
  // $res = str_replace('=<', '', $res);
  // $res = trim($res, '<=');
  // $res = str_replace('=&', '&', $res);
  // $res = str_replace('<', '', $res);
  // /*
  // return_code=SUCCESS/return_code&return_msg=OK/return_msg
  // */
  // $arr = explode('&', $res);
  // /*
  // return_code=SUCCESS/return_code
  // return_msg=OK/return_msg
  // */
  // $map = [];
  // foreach ($arr as $one) {
  //   $kv = explode('=', $one);
  //   $k = $kv[0];
  //   $v = $kv[1];
  //   $v_arr = explode('/', $v); //如果有多个/ 会出问题
  //   $map[$k] = $v_arr[0];
  // }
  // return $map;
  $res = str_replace('<![CDATA[]]>', '<![CDATA[-]]>', $res);
  // $res = str_replace(']]>', '', $res);
  $ob = simplexml_load_string($res, null, LIBXML_NOCDATA);
  return c_safe_to_json(json_encode($ob, JSON_UNESCAPED_UNICODE));
}

function s_make_wx_order($xml_str) {
  $res = curl_post_https('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml_str);
  Log::write($res . '==' . $xml_str, 'notice');
  return c_read_xml_to_map($res);
}


function s_refund_wx_order($xml_str) {
  $path_cert = dirname(dirname(__FILE__)) . Config::get('app.REFUND_CERT_FILE');
  $path_key = dirname(dirname(__FILE__)) . Config::get('app.REFUND_KEY_FILE');
  $res = curl_post_https('https://api.mch.weixin.qq.com/secapi/pay/refund', $xml_str, [
    'path_cert' => $path_cert,
    'path_key' => $path_key,
  ]);
  Log::write($res . '==' . $xml_str, 'notice');
  return c_read_xml_to_map($res);
}


function s_transfers_wx($xml_str) {
  $path_cert = dirname(dirname(__FILE__)) . Config::get('app.REFUND_CERT_FILE');
  $path_key = dirname(dirname(__FILE__)) . Config::get('app.REFUND_KEY_FILE');
  $res = curl_post_https('https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', $xml_str, [
    'path_cert' => $path_cert,
    'path_key' => $path_key,
  ]);
  Log::write($res . '==' . $xml_str, 'notice');
  return c_read_xml_to_map($res);
}

function s_v3_api_make_token ($url, $http_method, $body) {
    //$url = 'https://api.mch.weixin.qq.com/v3/certificates';
    // $http_method = 'GET';
    $timestamp = time();
    $nonce = random_vcode(32);
    // $body = '';
    $merchant_id = Config::get('app.MCH_ID');
    $serial_no = Config::get('app.CERT_SERIAL_NO');
    $mch_private_key = file_get_contents(dirname(dirname(__FILE__)) . Config::get('app.REFUND_KEY_FILE'));
    // exit();
    $url_parts = parse_url($url);
    $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
    $message = $http_method."\n".
    $canonical_url."\n".
    $timestamp."\n".
    $nonce."\n".
    $body."\n";
    
    openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
    $sign = base64_encode($raw_sign);
                        
    $schema = 'WECHATPAY2-SHA256-RSA2048';
    $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
    $merchant_id, $nonce, $timestamp, $serial_no, $sign);
    return $token;
}

function s_getEncrypt($str) {
	//$str是待加密字符串
	$public_key_path = dirname(dirname(__FILE__)) . Config::get('app.REFUND_CERT_FILE');
	$public_key = file_get_contents($public_key_path);
	$encrypted = '';
	if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
		//base64编码 
		$sign = base64_encode($encrypted);
	} else {
		throw new Exception('encrypt failed');
	}
	return $sign;
} 

function s_transfers_wx2($body) {
  $body_str = json_encode($body);
  $serial_no = Config::get('app.CERT_SERIAL_NO');
  $url = 'https://api.mch.weixin.qq.com/v3/transfer/batches';
  $token = s_v3_api_make_token($url,'POST', $body_str);
  $res = curl_post_https($url, $body_str, [
    'header' => [
      'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.39 KK/0.1',
      'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $token,
      'Accept: application/json',
      'Content-Type: application/json',
      'Wechatpay-Serial: '.s_getEncrypt($serial_no),
      'Accept-Language: zh-CN'
    ]
  ]);
  Log::write($res . '== s_transfers_wx2 == ' . $body_str, 'notice');
  // return c_read_xml_to_map($res);
  return c_safe_to_jsonx($res);
}




/**
* js鉴权获取access_token
*
* @return void
*/
function s_gzh_get_access_token() {
  $access_tken = Cache::get(Constant::$GZH_ACCESS_TOKEN);
  if ($access_tken) {
    return $access_tken;
  }
  $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . Config::get('app.GZH_APP_ID') . '&secret=' . Config::get('app.GZH_SECERT_KEY');
  $result = curl_get_https($url);
  //   c_debug($result);
  $result_json = json_decode($result, true);
  if (array_key_exists('errcode', $result_json)) {
    Log::write($result, 'WARN');
    return null;
  }
  $access_tken = $result_json['access_token'];
  Cache::set(Constant::$GZH_ACCESS_TOKEN, $access_tken, 7000);
  return $access_tken;
}

function s_gzh_get_jsticket() {
  $jsticket = Cache::get(Constant::$GZH_JS_TICKET);
  if ($jsticket) {
    return $jsticket;
  }
  Log::write('重新获取公众号ac了', 'notice');
  $access_tken = s_gzh_get_access_token();
  $url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' . $access_tken . '&type=jsapi';
  $result = curl_get_https($url);
  $result_json = json_decode($result, true);
  if (($result_json['errcode'])) {
    Log::write($result, 'WARN');
    return null;
  }
  $jsticket = $result_json['ticket'];
  Cache::set(Constant::$GZH_JS_TICKET, $jsticket, 7000);
  return $jsticket;
}



/**
* 用户授权获取access_token
*
* @param string $code
* @return array(access_token,openid)
*/
function s_base_auth($code) {
  //通过code换取网页授权access_token
  $data_str = curl_get_https('https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . Config::get('app.GZH_APP_ID') . '&secret=' . Config::get('app.GZH_SECERT_KEY') . '&code=' . $code . '&grant_type=authorization_code');
  $data_obj = json_decode($data_str, true);
  if (empty($data_obj) || array_key_exists('errcode', $data_obj)) {
    Log::write($data_str . '---SNS_OAUTH2_ERR', 'ERR');
    return null;
  }

  //获取成功
  // {"access_token":"ACCESS_TOKEN","expires_in":7200,"refresh_token":"REFRESH_TOKEN","openid":"OPENID","scope":"SCOPE" }
  $db_wx_auth = D('gzh_auth');
  //超找是否存在过openid
  $row_data = $db_wx_auth->where(array(
    'openid' => $data_obj['openid']
  ))->find();
  $unionid = $data_obj['unionid'] ?? '';
  $id = null;
  //如果没有存进去
  if (empty($row_data)) {
    $data = array(
      'unionid' => $unionid,
      'openid' => $data_obj['openid'],
      'refresh_token' => $data_obj['refresh_token'],
      'create_time' => date('Y-m-d H:i:s', time()),
    );
    $db_wx_auth->insert($data);
    $id = D('gzh_auth')->getLastInsID();
  }
  //如果存在,就更新下refresh_token
  else {
    $update = [
      //    'unionid' => $unionid,
      'refresh_token' => $data_obj['refresh_token'],
    ];
    if ($unionid) {
      $update['unionid'] = $unionid;
    }
    $db_wx_auth->where([
      'openid' => $data_obj['openid'],
    ])->update($update);
    $id = $row_data['id'];
  }

  return array(
    'access_token' => $data_obj['access_token'],
    'openid' => $data_obj['openid'],
    'id' => $id,
  );
}

function s_base_auth_tmp($code) {
  //通过code换取网页授权access_token
  $data_str = curl_get_https('https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . Config::get('app.GZH_APP_ID') . '&secret=' . Config::get('app.GZH_SECERT_KEY') . '&code=' . $code . '&grant_type=authorization_code');
  $data_obj = json_decode($data_str, true);
  if (empty($data_obj) || array_key_exists('errcode', $data_obj)) {
    Log::write($data_str . '---SNS_OAUTH2_ERR', 'ERR');
    return null;
  }

  //获取成功
  // {"access_token":"ACCESS_TOKEN","expires_in":7200,"refresh_token":"REFRESH_TOKEN","openid":"OPENID","scope":"SCOPE" }
  $db_wx_auth = D('gzh_auth_tmp');
  //超找是否存在过openid
  $row_data = $db_wx_auth->where(array(
    'openid' => $data_obj['openid']
  ))->find();
  $unionid = $data_obj['unionid'] ?? '';
  $id = null;
  //如果没有存进去
  if (empty($row_data)) {
    $data = array(
      'unionid' => $unionid,
      'openid' => $data_obj['openid'],
      'refresh_token' => $data_obj['refresh_token'],
      'create_time' => date('Y-m-d H:i:s', time()),
    );
    $db_wx_auth->insert($data);
    $id = D('gzh_auth')->getLastInsID();
  }
  //如果存在,就更新下refresh_token
  else {
    $update = [
      //    'unionid' => $unionid,
      'refresh_token' => $data_obj['refresh_token'],
    ];
    if ($unionid) {
      $update['unionid'] = $unionid;
    }
    $db_wx_auth->where([
      'openid' => $data_obj['openid'],
    ])->update($update);
    $id = $row_data['id'];
  }

  return array(
    'access_token' => $data_obj['access_token'],
    'openid' => $data_obj['openid'],
    'id' => $id,
  );
}

/**
* 拉取用户信息
*
* @param [string] $access_tken
* @return array
*/
function s_info_auth($code) {
  $base_auth_data = s_base_auth($code);
  //Log::write('ckaaaaaaaaaaaaaaaa1:' . microtime(),'NOTICE');
  $data_str = curl_get_https('https://api.weixin.qq.com/sns/userinfo?access_token=' . $base_auth_data['access_token'] . '&openid=' . $base_auth_data['openid'] . '&lang=zh_CN');
  //Log::write('ckaaaaaaaaaaaaaaaa2:' . microtime(),'NOTICE');
  /**
  "openid":" OPENID",
  " nickname": NICKNAME,
  "sex":"1",
  "province":"PROVINCE"
  "city":"CITY",
  "country":"COUNTRY",
  "headimgurl":"http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
  "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
  */
  $data_obj = json_decode($data_str, true);
  if (empty($data_obj) || array_key_exists('errcode', $data_obj)) {
    Log::write($data_str . '---SNS_OAUTH2_ERR-s_info_auth' . json_encode($base_auth_data), 'ERROR');
    return null;
  }
  $db_wx_auth = D('gzh_auth');
  //先找到openid
  $row_data = $db_wx_auth->where(array(
    'openid' => $data_obj['openid']
  ))->find();
  if ($data_obj['headimgurl'] == null) {
    return null;
  }
  //组装存入的数据
  $data = array(
    'nickname' => base64_encode($data_obj['nickname']), //base64下昵称解决emoij表情问题
    'sex' => $data_obj['sex'],
    'province' => $data_obj['province'],
    'city' => $data_obj['city'],
    'country' => $data_obj['country'],
    'headimgurl' => $data_obj['headimgurl'],
    'privilege' => json_encode($data_obj['privilege']),
    'unionid' => array_key_exists('unionid', $data_obj) ? $data_obj['unionid'] : null,
  );
  $db_wx_auth->where(array(
    'openid' => $data_obj['openid'],
  ))->save($data);
  return $row_data['id'] ? $row_data['id'] : null;
}

function c_debug($any, $tag = '', $exit = false) {
  Log::write($tag . '=:' . json_encode($any, JSON_UNESCAPED_UNICODE), 'notice');
  if ($exit) {
    exit();
  }
}

function s_send_temp_gzh_msg($tmpid, $openid, $data, $url = '') {
  $is_prod = false;
  if (env('app.envName', 'prod') == 'prod') {
    $is_prod = true;
  }
  if (!$is_prod) {
    $openid = 'oD5Hb0Tmjwsaxg5bVQEx7GoVAXwg';
  }
  Log::write('要发公众号消息了:' . json_encode([$tmpid, $openid, $data], JSON_UNESCAPED_UNICODE), 'notice');
  $gzh_ac_tk = Cache::get(Constant::$GZH_ACCESS_TOKEN);
  $adata = [];
  foreach ($data as $k => $one) {
    //Log::write('要发公众号消息了-221:'.json_encode($one,JSON_UNESCAPED_UNIs_base_authCODE),'notice');
    $adata[$k] = [
      'value' => $one[0],
      // 'color'=>$one[1] ?? '#333333'
    ];
    //  Log::write('要发公众号消息了-112:'.json_encode($one,JSON_UNESCAPED_UNICODE),'notice');
  }
  //Log::write('要发公众号消息了-2:'.json_encode($adata,JSON_UNESCAPED_UNICODE),'notice');
  $qdata = [
    "touser" => $openid,
    "template_id" => $tmpid,
    //   "url"=>$url,
    //   "miniprogram":{
    //      "appid":"xiaochengxuappid12345",
    //      "pagepath":"index?foo=bar"
    //   },
    "data" => $adata,
  ];
  if ($url) {
    $qdata['url'] = $url;
  }

  Log::write('要发公众号消息了-3:' . json_encode($qdata, JSON_UNESCAPED_UNICODE), 'notice');
  $re_data = curl_post_https('https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' . $gzh_ac_tk, json_encode($qdata));
  Log::write('发公众号消息返回:' . $re_data, 'notice');
  /*
       "first": {
                       "value":"恭喜你购买成功！",
                       "color":"#173177"
                   },
                   "keyword1":{
                       "value":"巧克力",
                       "color":"#173177"
                   },
                   */
}

function c_array_unset_tt($arr, $key = 'goods_id') {
  //建立一个目标数组
  $res = array();
  foreach ($arr as $value) {
    //查看有没有重复项
    if (isset($res[$value[$key]])) {
      unset($value[$key]); //有：销毁
    } else {
      $res[$value[$key]] = $value;
    }
  }
  return $res;
}

function c_star_first_str($str = '') {
  // echo ($str) ;exit();
  $str_re = str_repeat('*', mb_strlen($str) - 1);
  return mb_substr($str, 0, 1) . $str_re;
}

function c_max_len_3dot($str = '', $len = 20) {
  if (mb_strlen($str) <= $len) {
    return $str;
  }
  $str_2 = mb_substr($str, 0, $len - 3);
  return $str_2 . '...';
}

function c_sms_fix_str($str = '') {
  $str = str_replace('【', '(', $str);
  $str = str_replace('】', ')', $str);
  return $str;
}

function c_xml_to_json($xml_str) {
  return json_decode(json_encode(simplexml_load_string($xml_str, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
}


function c_formate_exception($e) {
  function trace_f($trace) {
    $xstr = '';
    $i = 0;
    foreach ($trace as $x) {
      $i++;
      $x['file'] = $x['file'] ?? '';
      $x['line'] = $x['line'] ?? '';
      $x['function'] = $x['function'] ?? '';
      $x['class'] = $x['class'] ?? '';
      $xstr .= "[#$i] {$x['file']}:{$x['line']} {$x['function']} {$x['class']}" . PHP_EOL;
    }
    return $xstr;
  }
  return 'errror: ' . $e->getMessage() . PHP_EOL
  . 'file: ' . $e->getFile() . ':' . $e->getLine() . PHP_EOL
  . 'trace: ' . PHP_EOL
  . trace_f($e->getTrace());
}


function c_safe_to_json($str, $catch_return = ['_' => '']) {
  try {
    $re = json_decode($str, true);
  } catch (Exception $e) {
    Log::write('转换json失败,c_safe_to_json:' . $str, 'WARN');
    return $catch_return;
  }
  if (json_last_error() !== JSON_ERROR_NONE) {
     return $catch_return;
  }
  return $re;
}

function c_json_encode($data) {
  return json_encode($data, JSON_UNESCAPED_UNICODE);
}

/**
* 身份证号验证
* @param $id
* @return bool
*/
function c_is_idcard($id) {
  $id = strtoupper($id);
  $regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/";
  $arr_split = array();
  if (!preg_match($regx, $id)) {
    return FALSE;
  }
  if (15 == strlen($id)) {
    //检查15位

    $regx = "/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/";

    @preg_match($regx, $id, $arr_split);
    //检查生日日期是否正确
    $dtm_birth = "19" . $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
    if (!strtotime($dtm_birth)) {
      return FALSE;
    } else {
      return TRUE;
    }
  } else {
    //检查18位

    $regx = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/";
    @preg_match($regx, $id, $arr_split);
    $dtm_birth = $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
    if (!strtotime($dtm_birth)) {
      //检查生日日期是否正确
      return FALSE;
    } else {
      //检验18位身份证的校验码是否正确。
      //校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
      $arr_int = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
      $arr_ch = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
      $sign = 0;
      for ($i = 0; $i < 17; $i++) {
        $b = intval(substr($id . '', $i, 1));
        $w = $arr_int[$i];
        $sign += $b * $w;
      }
      $n = $sign % 11;
      $val_num = $arr_ch[$n];
      if ($val_num != substr($id, 17, 1)) {
        return FALSE;
      } else {
        return TRUE;
      }
    }
  }
}


/**
* 银行支付通用方法
*/
function bank_to_pay($openid, $body, $price, $order_id, $notifyUrl) {
  // $appId = "8a81c1be744ea9950175dfd45869016a";
  // $appKey = "4c38129bb4c64e88a5045cdf9972df1f";
  $bank_appId = Config::get('app.BANK_APP_ID');
  $bank_appKey = Config::get('app.BANK_APP_KEY');

  $timestamp = date('YmdHis');
  $nonce = md5(uniqid(microtime(true), true));

  function getTimeString() {
    date_default_timezone_set("Asia/Shanghai");
    return date("Y-m-d H:i:s", time()); //yyyy-MM-dd HH:mm:ss
  }

  function getSignHeader($appId, $appKey, $timestamp, $nonce, $body) {
    $b = hash("sha256", json_encode($body), false);
    $c = $appId . $timestamp . $nonce . $b;
    $e = hash_hmac('sha256', $c, $appKey, true);
    $f = base64_encode($e);
    return 'authorization: OPEN-BODY-SIG AppId="' . $appId . '",Timestamp="' . $timestamp . '",Nonce="' . $nonce . '",Signature="' . $f . '"';
  }

  $appid = Config::get('app.APPID');

  $param = [
    'mid' => Config::get('app.BANK_MID'),
    'tid' => Config::get('app.BANK_TID'),
    'requestTimestamp' => date("Y-m-d H:i:s", time()),
    'instMid' => 'MINIDEFAULT',
    //业务类型
    'merOrderId' => '305F' . $order_id,
    //账单号
    'orderDesc' => $body,
    //账单描述
    'subOpenId' => $openid,
    //用户openID
    'totalAmount' => $price,
    //请求金额，整数，分为单位
    'subAppId' => $appid,
    //小程序APPID
    'tradeType' => 'MINI',
    'notifyUrl' => $notifyUrl,
  ];
  $authorization = getSignHeader($bank_appId, $bank_appKey, $timestamp, $nonce, $param);
  $param = json_encode($param);
  $url = Config::get('app.BANK_HOST') . '/v1/netpay/wx/unified-order';
  Log::write($param, 'NOTICE');
  $header[] = $authorization;
  $header[] = 'Accept:application/json';
  $header[] = 'Content-Type:application/json;charset=utf-8';
  $header[] = 'Content-Length:' . strlen($param);

  $curl = curl_init(); //初始化
  curl_setopt($curl, CURLOPT_URL, $url); //设置url
  curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
  curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在 设为0表示不检查证书 设为1表示检查证书中是否有CN(common name)字段 设为2表示在1的基础上校验当前的域名是否与CN匹配
  curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
  curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
  curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
  curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
  curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
  curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
  curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
  $reponse = curl_exec($curl);
  if ($reponse === false) {
    return "";
  }
  $reponse = json_decode($reponse, true);
  if ($reponse['errCode'] != 'SUCCESS') {
    Log::write(json_encode($reponse), 'ERROR');
    if ($reponse['errCode'] == 'DUP_ORDER') {
      return ['errCode' => -1,
        'errMsg' => '您已经支付过了,请稍后'];
    }
    throw new ErrorException('快快快,银行支付有问题!');
  }
  return $reponse;
}

/**
* 银行退款通用接口
*/
function bank_to_refund($order_id, $price) {
  // $appId = "8a81c1be744ea9950175dfd45869016a";
  // $appKey = "4c38129bb4c64e88a5045cdf9972df1f";
  $bank_pay_order = D('bank_pay_order')->where(['order_id' => $order_id])->find();
  if (empty($bank_pay_order)) {
    Log::write('$bank_pay_order 不存在:' . $order_id, 'ERROR');
    return ['errCode' => 'FAIL',
      'reason' => 'function:bank_to_refund,info not exist'];
  }
  $bank_order_id = $bank_pay_order['bank_order_id'];
  $bank_info = c_safe_to_json($bank_pay_order['bank_info']); //认为不会出错
  $bank_appId = $bank_info['BANK_APP_ID'];
  $bank_appKey = $bank_info['BANK_APP_KEY'];

  $timestamp = date('YmdHis');
  $nonce = md5(uniqid(microtime(true), true));

  function getTimeString() {
    date_default_timezone_set("Asia/Shanghai");
    return date("Y-m-d H:i:s", time()); //yyyy-MM-dd HH:mm:ss
  }

  function getSignHeader($appId, $appKey, $timestamp, $nonce, $body) {
    $b = hash("sha256", json_encode($body), false);
    $c = $appId . $timestamp . $nonce . $b;
    $e = hash_hmac('sha256', $c, $appKey, true);
    $f = base64_encode($e);
    return 'authorization: OPEN-BODY-SIG AppId="' . $appId . '",Timestamp="' . $timestamp . '",Nonce="' . $nonce . '",Signature="' . $f . '"';
  }

  $param = [
    'mid' => $bank_info['BANK_MID'],
    'tid' => $bank_info['BANK_TID'],
    'requestTimestamp' => date("Y-m-d H:i:s", time()),
    'merOrderId' => '305F' . $order_id,
    //账单号
    'targetOrderId' => $bank_order_id,
    //4200000918202101127103973012
    'refundAmount' => $price,
    //请求金额，整数，分为单位
    'refundOrderId' => '305F' . $order_id . 'D' . date("His", time()),
  ];
  $authorization = getSignHeader($bank_appId, $bank_appKey, $timestamp, $nonce, $param);
  $param = json_encode($param);
  $url = Config::get('app.BANK_HOST') . '/v1/netpay/refund';

  $header[] = $authorization;
  $header[] = 'Accept:application/json';
  $header[] = 'Content-Type:application/json;charset=utf-8';
  $header[] = 'Content-Length:' . strlen($param);

  $curl = curl_init(); //初始化
  curl_setopt($curl, CURLOPT_URL, $url); //设置url
  curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
  curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在 设为0表示不检查证书 设为1表示检查证书中是否有CN(common name)字段 设为2表示在1的基础上校验当前的域名是否与CN匹配
  curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
  curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
  curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
  curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
  curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
  curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
  curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
  $reponse = curl_exec($curl);
  if ($reponse === false) {
    return "";
  }
  $reponse = json_decode($reponse, true);
  return $reponse;
}



function bank_to_query_order($order_id) {

  $bank_info = Config::get('app.BANK_INFO_LIST')[Config::get('app.BANK_REF_ID')];
  $bank_appId = $bank_info['BANK_APP_ID'];
  $bank_appKey = $bank_info['BANK_APP_KEY'];

  $timestamp = date('YmdHis');
  $nonce = md5(uniqid(microtime(true), true));

  function getTimeString() {
    date_default_timezone_set("Asia/Shanghai");
    return date("Y-m-d H:i:s", time()); //yyyy-MM-dd HH:mm:ss
  }

  function getSignHeader($appId, $appKey, $timestamp, $nonce, $body) {
    $b = hash("sha256", json_encode($body), false);
    $c = $appId . $timestamp . $nonce . $b;
    $e = hash_hmac('sha256', $c, $appKey, true);
    $f = base64_encode($e);
    return 'authorization: OPEN-BODY-SIG AppId="' . $appId . '",Timestamp="' . $timestamp . '",Nonce="' . $nonce . '",Signature="' . $f . '"';
  }

  $param = [
    'mid' => $bank_info['BANK_MID'],
    'tid' => $bank_info['BANK_TID'],
    'requestTimestamp' => date("Y-m-d H:i:s", time()),
    'merOrderId' => '305F' . $order_id,
    //账单号
  ];
  $authorization = getSignHeader($bank_appId, $bank_appKey, $timestamp, $nonce, $param);
  $param = json_encode($param);
  $url = Config::get('app.BANK_HOST') . '/v1/netpay/query';

  $header[] = $authorization;
  $header[] = 'Accept:application/json';
  $header[] = 'Content-Type:application/json;charset=utf-8';
  $header[] = 'Content-Length:' . strlen($param);

  $curl = curl_init(); //初始化
  curl_setopt($curl, CURLOPT_URL, $url); //设置url
  curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
  curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在 设为0表示不检查证书 设为1表示检查证书中是否有CN(common name)字段 设为2表示在1的基础上校验当前的域名是否与CN匹配
  curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
  curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
  curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
  curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
  curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
  curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
  curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
  $reponse = curl_exec($curl);
  if ($reponse === false) {
    return "";
  }
  $reponse = json_decode($reponse, true);
  if ($reponse['errCode'] != 'SUCCESS') {
    throw new ErrorException('快快快,银行支付有问题!');
  }
  return $reponse;
}

function s_wx_create_mp_qrcode($scene_str = '', $scene_id = '', $action_name = '', $expire_seconds = 2592000) {
  $gzh_ac_tk = Cache::get(Constant::$GZH_ACCESS_TOKEN);
  $data = ['action_info' => ['scene' => []]];
  $data['action_name'] = $action_name;
  if ($action_name == 'QR_LIMIT_SCENE' || $action_name == 'QR_LIMIT_STR_SCENE') {
    //
  } else {
    $data['expire_seconds'] = $expire_seconds;
  }
  if ($scene_str) {
    if (!$data['action_name']) {
      $data['action_name'] = 'QR_STR_SCENE';
    }
    $data['action_info']['scene']['scene_str'] = $scene_str;
  } else {
    if ($scene_id) {
      if (!$data['action_name']) {
        $data['action_name'] = 'QR_SCENE';
      }
      $data['action_info']['scene']['scene_id'] = $scene_id;
    }
  }
  // print_r(json_encode($data));
  c_debug($data,'debug_xxxxxxxxxxx');
  $res = http_post_json('https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token='.$gzh_ac_tk, json_encode($data));
  // print_r($res);
  $json = c_safe_to_json($res);
  //{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm\3sUw==","expire_seconds":60,"url":"http://weixin.qq.com/q/kZgfwMTm72WWPkovabbI"}
  return $json;
}

function s_put_queue($type, $data) {}


function s_tstc_http($url, $data, $pid, $auth_key) {
  $data['_pid'] = $pid;
  ksort($data);
  $p_list = [$auth_key];
  foreach ($data as $key => $one) {
    if (is_array($one)) {
      ksort($one);
      foreach ($one as $k2 => $v2) {
        ksort($v2);
        $one[$k2] = $v2;
      }
      $data[$key] = $one;
    }
  }
  foreach ($data as $key => $one) {
    $t_one = $one;
    if (is_array($one)) {
      $t_one = '[';
      foreach ($one as $k => $v) {
        $t_one .= '' . $k . '=[';
        $p2_list = [];
        foreach ($v as $k2 => $v2) {
          $p2_list[] = $k2 . '=' . $v2;
        }
        $t_one .= implode('&', $p2_list);
        $t_one .= ']';
      }
      $t_one .= ']';
    }
    $p_list[] = $key . '=' . $t_one;
  }
  $p_list[] = $auth_key;
  echo $p_str = implode('&', $p_list);
  // $p_str = http_build_query($data);
  // echo $p_str = urldecode($p_str);
  $data['_sig'] = md5($p_str);
  //echo http_build_query( $data );
  $ci = curl_init();
  curl_setopt_array($ci, array(
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_0,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => 'gzip',
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HEADER => false,
    CURLINFO_HEADER_OUT => true,
  ));
  curl_setopt($ci, CURLOPT_POST, true);
  curl_setopt($ci, CURLOPT_POSTFIELDS, http_build_query($data));
  curl_setopt($ci, CURLOPT_URL, $url);
  $response = curl_exec($ci);
  curl_close($ci);
  if ($response) {
    return c_safe_to_json($response);
  }
  Log::write('天时同城请求错误:' . json_decode($url, $data, $pid), 'ERROR');
  return ['_' => '_'];
}


function qs_http_post($host, $port, $query, $body) {
  $fp = fsockopen($host, $port, $errno, $errstr, 1);
  if (!$fp) {
    return false;
  }
  $header = '';
  $out = "POST ${query} HTTP/1.1\r\n";
  $out .= "Host: ${host}\r\n";
  $out .= "Content-Length: " . strlen($body) . "\r\n";
  $out .= "Connection: close\r\n";
  $out .= "\r\n";
  $out .= $body;
  fwrite($fp, $out);
  $line = trim(fgets($fp));
  $header .= $line;
  list($proto, $rcode, $result) = explode(" ", $line);
  $len = -1;
  while (($line = trim(fgets($fp))) != "") {
    $header .= $line;
    if (strstr($line, "Content-Length:")) {
      list($cl, $len) = explode(" ", $line);
    }
    if (strstr($line, "Connection: close")) {
      $close = true;
    }
  }
  if ($len < 0) {
    return false;
  }
  $body = @fread($fp, $len);
  if ($close) fclose($fp);
  return $body;
}

function qs_put($host, $port, $charset = 'utf-8', $name, $data) {
  $result = qs_http_post($host, $port, "/?charset=".$charset."&name=".$name."&opt=put", $data);
  if ($result == "HTTPSQS_PUT_OK") {
    return true;
  }
  return false;
}
function c_now() {
  return c_date(time());
}
function c_date($time) {
  $time = $time ?: time();
  return date("Y-m-d H:i:s", $time);
}